From b6c5eb4ab78d2d1caa54486afaeefc0aef98a898 Mon Sep 17 00:00:00 2001 From: Reuben Bond <203839+ReubenBond@users.noreply.github.com> Date: Fri, 5 Apr 2024 15:34:43 -0700 Subject: [PATCH] Service Discovery: Implement approved API (#3413) * Rename EndPoint to Endpoint, resolver to provider * Apply changes decided during API review * Find & fix straggler file names * Variables and members assignable to System.Net.EndPoint use upper-case P * Update src/Microsoft.Extensions.ServiceDiscovery.Abstractions/ServiceEndpoint.cs Co-authored-by: Stephen Halter * Delete GetEndpointString() --------- Co-authored-by: Stephen Halter --- .../IServiceEndPointProviderFactory.cs | 20 -- ...tBuilder.cs => IServiceEndpointBuilder.cs} | 8 +- ...rovider.cs => IServiceEndpointProvider.cs} | 6 +- .../IServiceEndpointProviderFactory.cs | 20 ++ ...EndPointImpl.cs => ServiceEndpointImpl.cs} | 8 +- .../README.md | 2 +- ...{ServiceEndPoint.cs => ServiceEndpoint.cs} | 21 +-- ...dPointQuery.cs => ServiceEndpointQuery.cs} | 35 ++-- ...ointSource.cs => ServiceEndpointSource.cs} | 16 +- .../DnsServiceEndPointResolverProvider.cs | 21 --- ...olver.cs => DnsServiceEndpointProvider.cs} | 28 +-- ... => DnsServiceEndpointProviderBase.Log.cs} | 2 +- ...e.cs => DnsServiceEndpointProviderBase.cs} | 36 ++-- .../DnsServiceEndpointProviderFactory.cs | 21 +++ ...s => DnsServiceEndpointProviderOptions.cs} | 6 +- ...er.cs => DnsSrvServiceEndpointProvider.cs} | 34 ++-- ...> DnsSrvServiceEndpointProviderFactory.cs} | 19 +- ...> DnsSrvServiceEndpointProviderOptions.cs} | 6 +- .../README.md | 16 +- ...DiscoveryDnsServiceCollectionExtensions.cs | 33 +++- .../ServiceDiscoveryDestinationResolver.cs | 10 +- ...nfigurationServiceEndpointProvider.Log.cs} | 12 +- ...> ConfigurationServiceEndpointProvider.cs} | 64 +++---- ...gurationServiceEndpointProviderFactory.cs} | 12 +- ...erviceEndpointProviderOptionsValidator.cs} | 10 +- ...gurationServiceEndpointProviderOptions.cs} | 6 +- ...lver.cs => HttpServiceEndpointResolver.cs} | 46 ++--- ...rviceDiscoveryHttpMessageHandlerFactory.cs | 2 +- .../Http/ResolvingHttpClientHandler.cs | 6 +- .../Http/ResolvingHttpDelegatingHandler.cs | 20 +- ...rviceDiscoveryHttpMessageHandlerFactory.cs | 4 +- ...lt.cs => ServiceEndpointResolverResult.cs} | 8 +- ...elector.cs => IServiceEndpointSelector.cs} | 10 +- ...s => RoundRobinServiceEndpointSelector.cs} | 12 +- ...PassThroughServiceEndpointProvider.Log.cs} | 4 +- ... => PassThroughServiceEndpointProvider.cs} | 14 +- ...sThroughServiceEndpointProviderFactory.cs} | 20 +- .../README.md | 76 ++++---- ...iceDiscoveryHttpClientBuilderExtensions.cs | 4 +- .../ServiceDiscoveryOptions.cs | 23 +-- ...iceDiscoveryServiceCollectionExtensions.cs | 40 ++-- .../ServiceEndPointWatcherFactory.cs | 61 ------ ...ntBuilder.cs => ServiceEndpointBuilder.cs} | 12 +- ...Resolver.cs => ServiceEndpointResolver.cs} | 36 ++-- ...r.Log.cs => ServiceEndpointWatcher.Log.cs} | 24 +-- ...ntWatcher.cs => ServiceEndpointWatcher.cs} | 80 ++++---- ...s => ServiceEndpointWatcherFactory.Log.cs} | 11 +- .../ServiceEndpointWatcherFactory.cs | 61 ++++++ ... => DnsSrvServiceEndpointResolverTests.cs} | 125 ++++-------- ...figurationServiceEndpointResolverTests.cs} | 178 +++++++++--------- ...assThroughServiceEndpointResolverTests.cs} | 74 ++++---- ...sts.cs => ServiceEndpointResolverTests.cs} | 150 +++++++-------- 52 files changed, 763 insertions(+), 810 deletions(-) delete mode 100644 src/Microsoft.Extensions.ServiceDiscovery.Abstractions/IServiceEndPointProviderFactory.cs rename src/Microsoft.Extensions.ServiceDiscovery.Abstractions/{IServiceEndPointBuilder.cs => IServiceEndpointBuilder.cs} (80%) rename src/Microsoft.Extensions.ServiceDiscovery.Abstractions/{IServiceEndPointProvider.cs => IServiceEndpointProvider.cs} (75%) create mode 100644 src/Microsoft.Extensions.ServiceDiscovery.Abstractions/IServiceEndpointProviderFactory.cs rename src/Microsoft.Extensions.ServiceDiscovery.Abstractions/Internal/{ServiceEndPointImpl.cs => ServiceEndpointImpl.cs} (60%) rename src/Microsoft.Extensions.ServiceDiscovery.Abstractions/{ServiceEndPoint.cs => ServiceEndpoint.cs} (52%) rename src/Microsoft.Extensions.ServiceDiscovery.Abstractions/{ServiceEndPointQuery.cs => ServiceEndpointQuery.cs} (69%) rename src/Microsoft.Extensions.ServiceDiscovery.Abstractions/{ServiceEndPointSource.cs => ServiceEndpointSource.cs} (74%) delete mode 100644 src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndPointResolverProvider.cs rename src/Microsoft.Extensions.ServiceDiscovery.Dns/{DnsServiceEndPointResolver.cs => DnsServiceEndpointProvider.cs} (61%) rename src/Microsoft.Extensions.ServiceDiscovery.Dns/{DnsServiceEndPointResolverBase.Log.cs => DnsServiceEndpointProviderBase.Log.cs} (97%) rename src/Microsoft.Extensions.ServiceDiscovery.Dns/{DnsServiceEndPointResolverBase.cs => DnsServiceEndpointProviderBase.cs} (81%) create mode 100644 src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndpointProviderFactory.cs rename src/Microsoft.Extensions.ServiceDiscovery.Dns/{DnsServiceEndPointResolverOptions.cs => DnsServiceEndpointProviderOptions.cs} (84%) rename src/Microsoft.Extensions.ServiceDiscovery.Dns/{DnsSrvServiceEndPointResolver.cs => DnsSrvServiceEndpointProvider.cs} (71%) rename src/Microsoft.Extensions.ServiceDiscovery.Dns/{DnsSrvServiceEndPointResolverProvider.cs => DnsSrvServiceEndpointProviderFactory.cs} (86%) rename src/Microsoft.Extensions.ServiceDiscovery.Dns/{DnsSrvServiceEndPointResolverOptions.cs => DnsSrvServiceEndpointProviderOptions.cs} (86%) rename src/Microsoft.Extensions.ServiceDiscovery/Configuration/{ConfigurationServiceEndPointResolver.Log.cs => ConfigurationServiceEndpointProvider.Log.cs} (78%) rename src/Microsoft.Extensions.ServiceDiscovery/Configuration/{ConfigurationServiceEndPointResolver.cs => ConfigurationServiceEndpointProvider.cs} (76%) rename src/Microsoft.Extensions.ServiceDiscovery/Configuration/{ConfigurationServiceEndPointResolverProvider.cs => ConfigurationServiceEndpointProviderFactory.cs} (58%) rename src/Microsoft.Extensions.ServiceDiscovery/Configuration/{ConfigurationServiceEndPointResolverOptionsValidator.cs => ConfigurationServiceEndpointProviderOptionsValidator.cs} (62%) rename src/Microsoft.Extensions.ServiceDiscovery/{ConfigurationServiceEndPointResolverOptions.cs => ConfigurationServiceEndpointProviderOptions.cs} (75%) rename src/Microsoft.Extensions.ServiceDiscovery/Http/{HttpServiceEndPointResolver.cs => HttpServiceEndpointResolver.cs} (82%) rename src/Microsoft.Extensions.ServiceDiscovery/Internal/{ServiceEndPointResolverResult.cs => ServiceEndpointResolverResult.cs} (73%) rename src/Microsoft.Extensions.ServiceDiscovery/LoadBalancing/{IServiceEndPointSelector.cs => IServiceEndpointSelector.cs} (69%) rename src/Microsoft.Extensions.ServiceDiscovery/LoadBalancing/{RoundRobinServiceEndPointSelector.cs => RoundRobinServiceEndpointSelector.cs} (63%) rename src/Microsoft.Extensions.ServiceDiscovery/PassThrough/{PassThroughServiceEndPointResolver.Log.cs => PassThroughServiceEndpointProvider.Log.cs} (78%) rename src/Microsoft.Extensions.ServiceDiscovery/PassThrough/{PassThroughServiceEndPointResolver.cs => PassThroughServiceEndpointProvider.cs} (60%) rename src/Microsoft.Extensions.ServiceDiscovery/PassThrough/{PassThroughServiceEndPointResolverProvider.cs => PassThroughServiceEndpointProviderFactory.cs} (70%) delete mode 100644 src/Microsoft.Extensions.ServiceDiscovery/ServiceEndPointWatcherFactory.cs rename src/Microsoft.Extensions.ServiceDiscovery/{ServiceEndPointBuilder.cs => ServiceEndpointBuilder.cs} (75%) rename src/Microsoft.Extensions.ServiceDiscovery/{ServiceEndPointResolver.cs => ServiceEndpointResolver.cs} (84%) rename src/Microsoft.Extensions.ServiceDiscovery/{ServiceEndPointWatcher.Log.cs => ServiceEndpointWatcher.Log.cs} (60%) rename src/Microsoft.Extensions.ServiceDiscovery/{ServiceEndPointWatcher.cs => ServiceEndpointWatcher.cs} (75%) rename src/Microsoft.Extensions.ServiceDiscovery/{ServiceEndPointWatcherFactory.Log.cs => ServiceEndpointWatcherFactory.Log.cs} (53%) create mode 100644 src/Microsoft.Extensions.ServiceDiscovery/ServiceEndpointWatcherFactory.cs rename tests/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/{DnsSrvServiceEndPointResolverTests.cs => DnsSrvServiceEndpointResolverTests.cs} (72%) rename tests/Microsoft.Extensions.ServiceDiscovery.Tests/{ConfigurationServiceEndPointResolverTests.cs => ConfigurationServiceEndpointResolverTests.cs} (60%) rename tests/Microsoft.Extensions.ServiceDiscovery.Tests/{PassThroughServiceEndPointResolverTests.cs => PassThroughServiceEndpointResolverTests.cs} (60%) rename tests/Microsoft.Extensions.ServiceDiscovery.Tests/{ServiceEndPointResolverTests.cs => ServiceEndpointResolverTests.cs} (62%) diff --git a/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/IServiceEndPointProviderFactory.cs b/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/IServiceEndPointProviderFactory.cs deleted file mode 100644 index 4b1876f808..0000000000 --- a/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/IServiceEndPointProviderFactory.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics.CodeAnalysis; - -namespace Microsoft.Extensions.ServiceDiscovery; - -/// -/// Creates instances. -/// -public interface IServiceEndPointProviderFactory -{ - /// - /// Tries to create an instance for the specified . - /// - /// The service to create the resolver for. - /// The resolver. - /// if the resolver was created, otherwise. - bool TryCreateProvider(ServiceEndPointQuery query, [NotNullWhen(true)] out IServiceEndPointProvider? resolver); -} diff --git a/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/IServiceEndPointBuilder.cs b/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/IServiceEndpointBuilder.cs similarity index 80% rename from src/Microsoft.Extensions.ServiceDiscovery.Abstractions/IServiceEndPointBuilder.cs rename to src/Microsoft.Extensions.ServiceDiscovery.Abstractions/IServiceEndpointBuilder.cs index 468adea1c0..e051b2bf74 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/IServiceEndPointBuilder.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/IServiceEndpointBuilder.cs @@ -7,14 +7,14 @@ namespace Microsoft.Extensions.ServiceDiscovery; /// -/// Builder to create a instances. +/// Builder to create a instances. /// -public interface IServiceEndPointBuilder +public interface IServiceEndpointBuilder { /// /// Gets the endpoints. /// - IList EndPoints { get; } + IList Endpoints { get; } /// /// Gets the feature collection. @@ -22,7 +22,7 @@ public interface IServiceEndPointBuilder IFeatureCollection Features { get; } /// - /// Adds a change token to the resulting . + /// Adds a change token to the resulting . /// /// The change token. void AddChangeToken(IChangeToken changeToken); diff --git a/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/IServiceEndPointProvider.cs b/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/IServiceEndpointProvider.cs similarity index 75% rename from src/Microsoft.Extensions.ServiceDiscovery.Abstractions/IServiceEndPointProvider.cs rename to src/Microsoft.Extensions.ServiceDiscovery.Abstractions/IServiceEndpointProvider.cs index 950823257a..4a192180b6 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/IServiceEndPointProvider.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/IServiceEndpointProvider.cs @@ -6,13 +6,13 @@ namespace Microsoft.Extensions.ServiceDiscovery; /// /// Provides details about a service's endpoints. /// -public interface IServiceEndPointProvider : IAsyncDisposable +public interface IServiceEndpointProvider : IAsyncDisposable { /// /// Resolves the endpoints for the service. /// - /// The endpoint collection, which resolved endpoints will be added to. + /// The endpoint collection, which resolved endpoints will be added to. /// The token to monitor for cancellation requests. /// The resolution status. - ValueTask PopulateAsync(IServiceEndPointBuilder endPoints, CancellationToken cancellationToken); + ValueTask PopulateAsync(IServiceEndpointBuilder endpoints, CancellationToken cancellationToken); } diff --git a/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/IServiceEndpointProviderFactory.cs b/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/IServiceEndpointProviderFactory.cs new file mode 100644 index 0000000000..009cbf05d7 --- /dev/null +++ b/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/IServiceEndpointProviderFactory.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; + +namespace Microsoft.Extensions.ServiceDiscovery; + +/// +/// Creates instances. +/// +public interface IServiceEndpointProviderFactory +{ + /// + /// Tries to create an instance for the specified . + /// + /// The service to create the provider for. + /// The provider. + /// if the provider was created, otherwise. + bool TryCreateProvider(ServiceEndpointQuery query, [NotNullWhen(true)] out IServiceEndpointProvider? provider); +} diff --git a/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/Internal/ServiceEndPointImpl.cs b/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/Internal/ServiceEndpointImpl.cs similarity index 60% rename from src/Microsoft.Extensions.ServiceDiscovery.Abstractions/Internal/ServiceEndPointImpl.cs rename to src/Microsoft.Extensions.ServiceDiscovery.Abstractions/Internal/ServiceEndpointImpl.cs index 7d135dfe97..8bfb50fe93 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/Internal/ServiceEndPointImpl.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/Internal/ServiceEndpointImpl.cs @@ -6,9 +6,13 @@ namespace Microsoft.Extensions.ServiceDiscovery.Internal; -internal sealed class ServiceEndPointImpl(EndPoint endPoint, IFeatureCollection? features = null) : ServiceEndPoint +internal sealed class ServiceEndpointImpl(EndPoint endPoint, IFeatureCollection? features = null) : ServiceEndpoint { public override EndPoint EndPoint { get; } = endPoint; public override IFeatureCollection Features { get; } = features ?? new FeatureCollection(); - public override string? ToString() => GetEndPointString(); + public override string? ToString() => EndPoint switch + { + DnsEndPoint dns => $"{dns.Host}:{dns.Port}", + _ => EndPoint.ToString()! + }; } diff --git a/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/README.md b/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/README.md index c5cf6b9bc7..0d97211313 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/README.md +++ b/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/README.md @@ -1,6 +1,6 @@ # Microsoft.Extensions.ServiceDiscovery.Abstractions -The `Microsoft.Extensions.ServiceDiscovery.Abstractions` library provides abstractions used by the `Microsoft.Extensions.ServiceDiscovery` library and other libraries which implement service discovery extensions, such as service endpoint resolvers. For more information, see [Service discovery in .NET](https://learn.microsoft.com/dotnet/core/extensions/service-discovery). +The `Microsoft.Extensions.ServiceDiscovery.Abstractions` library provides abstractions used by the `Microsoft.Extensions.ServiceDiscovery` library and other libraries which implement service discovery extensions, such as service endpoint providers. For more information, see [Service discovery in .NET](https://learn.microsoft.com/dotnet/core/extensions/service-discovery). ## Feedback & contributing diff --git a/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/ServiceEndPoint.cs b/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/ServiceEndpoint.cs similarity index 52% rename from src/Microsoft.Extensions.ServiceDiscovery.Abstractions/ServiceEndPoint.cs rename to src/Microsoft.Extensions.ServiceDiscovery.Abstractions/ServiceEndpoint.cs index a3cde62ce0..238e383a95 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/ServiceEndPoint.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/ServiceEndpoint.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics; using System.Net; using Microsoft.AspNetCore.Http.Features; using Microsoft.Extensions.ServiceDiscovery.Internal; @@ -11,8 +10,7 @@ namespace Microsoft.Extensions.ServiceDiscovery; /// /// Represents an endpoint for a service. /// -[DebuggerDisplay("{GetEndPointString(),nq}")] -public abstract class ServiceEndPoint +public abstract class ServiceEndpoint { /// /// Gets the endpoint. @@ -25,21 +23,10 @@ public abstract class ServiceEndPoint public abstract IFeatureCollection Features { get; } /// - /// Creates a new . + /// Creates a new . /// /// The endpoint being represented. /// Features of the endpoint. - /// A newly initialized . - public static ServiceEndPoint Create(EndPoint endPoint, IFeatureCollection? features = null) => new ServiceEndPointImpl(endPoint, features); - - /// - /// Gets a string representation of the . - /// - /// A string representation of the . - public virtual string GetEndPointString() => EndPoint switch - { - DnsEndPoint dns => $"{dns.Host}:{dns.Port}", - IPEndPoint ip => ip.ToString(), - _ => EndPoint.ToString()! - }; + /// A newly initialized . + public static ServiceEndpoint Create(EndPoint endPoint, IFeatureCollection? features = null) => new ServiceEndpointImpl(endPoint, features); } diff --git a/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/ServiceEndPointQuery.cs b/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/ServiceEndpointQuery.cs similarity index 69% rename from src/Microsoft.Extensions.ServiceDiscovery.Abstractions/ServiceEndPointQuery.cs rename to src/Microsoft.Extensions.ServiceDiscovery.Abstractions/ServiceEndpointQuery.cs index 99c92cce27..600dc5cc28 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/ServiceEndPointQuery.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/ServiceEndpointQuery.cs @@ -8,21 +8,23 @@ namespace Microsoft.Extensions.ServiceDiscovery; /// /// Describes a query for endpoints of a service. /// -public sealed class ServiceEndPointQuery +public sealed class ServiceEndpointQuery { + private readonly string _originalString; + /// - /// Initializes a new instance. + /// Initializes a new instance. /// /// The string which the query was constructed from. /// The ordered list of included URI schemes. /// The service name. - /// The optional endpoint name. - private ServiceEndPointQuery(string originalString, string[] includedSchemes, string serviceName, string? endPointName) + /// The optional endpoint name. + private ServiceEndpointQuery(string originalString, string[] includedSchemes, string serviceName, string? endpointName) { - OriginalString = originalString; - IncludeSchemes = includedSchemes; + _originalString = originalString; + IncludedSchemes = includedSchemes; ServiceName = serviceName; - EndPointName = endPointName; + EndpointName = endpointName; } /// @@ -31,7 +33,7 @@ private ServiceEndPointQuery(string originalString, string[] includedSchemes, st /// The value to parse. /// The resulting query. /// if the value was successfully parsed; otherwise . - public static bool TryParse(string input, [NotNullWhen(true)] out ServiceEndPointQuery? query) + public static bool TryParse(string input, [NotNullWhen(true)] out ServiceEndpointQuery? query) { bool hasScheme; if (!input.Contains("://", StringComparison.InvariantCulture) @@ -52,10 +54,10 @@ public static bool TryParse(string input, [NotNullWhen(true)] out ServiceEndPoin var uriHost = uri.Host; var segmentSeparatorIndex = uriHost.IndexOf('.'); string host; - string? endPointName = null; + string? endpointName = null; if (uriHost.StartsWith('_') && segmentSeparatorIndex > 1 && uriHost[^1] != '.') { - endPointName = uriHost[1..segmentSeparatorIndex]; + endpointName = uriHost[1..segmentSeparatorIndex]; // Skip the endpoint name, including its prefix ('_') and suffix ('.'). host = uriHost[(segmentSeparatorIndex + 1)..]; @@ -67,24 +69,19 @@ public static bool TryParse(string input, [NotNullWhen(true)] out ServiceEndPoin // Allow multiple schemes to be separated by a '+', eg. "https+http://host:port". var schemes = hasScheme ? uri.Scheme.Split('+') : []; - query = new(input, schemes, host, endPointName); + query = new(input, schemes, host, endpointName); return true; } - /// - /// Gets the string which the query was constructed from. - /// - public string OriginalString { get; } - /// /// Gets the ordered list of included URI schemes. /// - public IReadOnlyList IncludeSchemes { get; } + public IReadOnlyList IncludedSchemes { get; } /// /// Gets the endpoint name, or if no endpoint name is specified. /// - public string? EndPointName { get; } + public string? EndpointName { get; } /// /// Gets the service name. @@ -92,6 +89,6 @@ public static bool TryParse(string input, [NotNullWhen(true)] out ServiceEndPoin public string ServiceName { get; } /// - public override string? ToString() => EndPointName is not null ? $"Service: {ServiceName}, Endpoint: {EndPointName}, Schemes: {string.Join(", ", IncludeSchemes)}" : $"Service: {ServiceName}, Schemes: {string.Join(", ", IncludeSchemes)}"; + public override string? ToString() => _originalString; } diff --git a/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/ServiceEndPointSource.cs b/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/ServiceEndpointSource.cs similarity index 74% rename from src/Microsoft.Extensions.ServiceDiscovery.Abstractions/ServiceEndPointSource.cs rename to src/Microsoft.Extensions.ServiceDiscovery.Abstractions/ServiceEndpointSource.cs index 807981226e..fb5bff1b28 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/ServiceEndPointSource.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery.Abstractions/ServiceEndpointSource.cs @@ -11,18 +11,18 @@ namespace Microsoft.Extensions.ServiceDiscovery; /// Represents a collection of service endpoints. /// [DebuggerDisplay("{ToString(),nq}")] -[DebuggerTypeProxy(typeof(ServiceEndPointCollectionDebuggerView))] -public sealed class ServiceEndPointSource +[DebuggerTypeProxy(typeof(ServiceEndpointCollectionDebuggerView))] +public sealed class ServiceEndpointSource { - private readonly List? _endpoints; + private readonly List? _endpoints; /// - /// Initializes a new instance. + /// Initializes a new instance. /// /// The endpoints. /// The change token. /// The feature collection. - public ServiceEndPointSource(List? endpoints, IChangeToken changeToken, IFeatureCollection features) + public ServiceEndpointSource(List? endpoints, IChangeToken changeToken, IFeatureCollection features) { ArgumentNullException.ThrowIfNull(changeToken); @@ -34,7 +34,7 @@ public ServiceEndPointSource(List? endpoints, IChangeToken chan /// /// Gets the endpoints. /// - public IReadOnlyList EndPoints => _endpoints ?? (IReadOnlyList)[]; + public IReadOnlyList Endpoints => _endpoints ?? (IReadOnlyList)[]; /// /// Gets the change token which indicates when this collection should be refreshed. @@ -57,13 +57,13 @@ public override string ToString() return $"[{string.Join(", ", eps)}]"; } - private sealed class ServiceEndPointCollectionDebuggerView(ServiceEndPointSource value) + private sealed class ServiceEndpointCollectionDebuggerView(ServiceEndpointSource value) { public IChangeToken ChangeToken => value.ChangeToken; public IFeatureCollection Features => value.Features; [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] - public ServiceEndPoint[] EndPoints => value.EndPoints.ToArray(); + public ServiceEndpoint[] Endpoints => value.Endpoints.ToArray(); } } diff --git a/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndPointResolverProvider.cs b/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndPointResolverProvider.cs deleted file mode 100644 index 51525663a0..0000000000 --- a/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndPointResolverProvider.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics.CodeAnalysis; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; - -namespace Microsoft.Extensions.ServiceDiscovery.Dns; - -internal sealed partial class DnsServiceEndPointResolverProvider( - IOptionsMonitor options, - ILogger logger, - TimeProvider timeProvider) : IServiceEndPointProviderFactory -{ - /// - public bool TryCreateProvider(ServiceEndPointQuery query, [NotNullWhen(true)] out IServiceEndPointProvider? resolver) - { - resolver = new DnsServiceEndPointResolver(query.OriginalString, hostName: query.ServiceName, options, logger, timeProvider); - return true; - } -} diff --git a/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndPointResolver.cs b/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndpointProvider.cs similarity index 61% rename from src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndPointResolver.cs rename to src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndpointProvider.cs index a2601c84b4..6cc9f92bc4 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndPointResolver.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndpointProvider.cs @@ -7,12 +7,12 @@ namespace Microsoft.Extensions.ServiceDiscovery.Dns; -internal sealed partial class DnsServiceEndPointResolver( - string serviceName, +internal sealed partial class DnsServiceEndpointProvider( + ServiceEndpointQuery query, string hostName, - IOptionsMonitor options, - ILogger logger, - TimeProvider timeProvider) : DnsServiceEndPointResolverBase(serviceName, logger, timeProvider), IHostNameFeature + IOptionsMonitor options, + ILogger logger, + TimeProvider timeProvider) : DnsServiceEndpointProviderBase(query, logger, timeProvider), IHostNameFeature { protected override double RetryBackOffFactor => options.CurrentValue.RetryBackOffFactor; protected override TimeSpan MinRetryPeriod => options.CurrentValue.MinRetryPeriod; @@ -26,27 +26,27 @@ internal sealed partial class DnsServiceEndPointResolver( protected override async Task ResolveAsyncCore() { - var endPoints = new List(); + var endpoints = new List(); var ttl = DefaultRefreshPeriod; Log.AddressQuery(logger, ServiceName, hostName); var addresses = await System.Net.Dns.GetHostAddressesAsync(hostName, ShutdownToken).ConfigureAwait(false); foreach (var address in addresses) { - var serviceEndPoint = ServiceEndPoint.Create(new IPEndPoint(address, 0)); - serviceEndPoint.Features.Set(this); - if (options.CurrentValue.ApplyHostNameMetadata(serviceEndPoint)) + var serviceEndpoint = ServiceEndpoint.Create(new IPEndPoint(address, 0)); + serviceEndpoint.Features.Set(this); + if (options.CurrentValue.ShouldApplyHostNameMetadata(serviceEndpoint)) { - serviceEndPoint.Features.Set(this); + serviceEndpoint.Features.Set(this); } - endPoints.Add(serviceEndPoint); + endpoints.Add(serviceEndpoint); } - if (endPoints.Count == 0) + if (endpoints.Count == 0) { - throw new InvalidOperationException($"No DNS records were found for service {ServiceName} (DNS name: {hostName})."); + throw new InvalidOperationException($"No DNS records were found for service '{ServiceName}' (DNS name: '{hostName}')."); } - SetResult(endPoints, ttl); + SetResult(endpoints, ttl); } } diff --git a/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndPointResolverBase.Log.cs b/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndpointProviderBase.Log.cs similarity index 97% rename from src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndPointResolverBase.Log.cs rename to src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndpointProviderBase.Log.cs index cd664215aa..29aaaf8e93 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndPointResolverBase.Log.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndpointProviderBase.Log.cs @@ -5,7 +5,7 @@ namespace Microsoft.Extensions.ServiceDiscovery.Dns; -partial class DnsServiceEndPointResolverBase +partial class DnsServiceEndpointProviderBase { internal static partial class Log { diff --git a/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndPointResolverBase.cs b/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndpointProviderBase.cs similarity index 81% rename from src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndPointResolverBase.cs rename to src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndpointProviderBase.cs index 9d6c54e475..6c69cc7a76 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndPointResolverBase.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndpointProviderBase.cs @@ -7,9 +7,9 @@ namespace Microsoft.Extensions.ServiceDiscovery.Dns; /// -/// A service end point resolver that uses DNS to resolve the service end points. +/// A service end point provider that uses DNS to resolve the service end points. /// -internal abstract partial class DnsServiceEndPointResolverBase : IServiceEndPointProvider +internal abstract partial class DnsServiceEndpointProviderBase : IServiceEndpointProvider { private readonly object _lock = new(); private readonly ILogger _logger; @@ -20,23 +20,23 @@ internal abstract partial class DnsServiceEndPointResolverBase : IServiceEndPoin private bool _hasEndpoints; private CancellationChangeToken _lastChangeToken; private CancellationTokenSource _lastCollectionCancellation; - private List? _lastEndPointCollection; + private List? _lastEndpointCollection; private TimeSpan _nextRefreshPeriod; /// - /// Initializes a new instance. + /// Initializes a new instance. /// - /// The service name. + /// The service name. /// The logger. /// The time provider. - protected DnsServiceEndPointResolverBase( - string serviceName, + protected DnsServiceEndpointProviderBase( + ServiceEndpointQuery query, ILogger logger, TimeProvider timeProvider) { - ServiceName = serviceName; + ServiceName = query.ToString()!; _logger = logger; - _lastEndPointCollection = null; + _lastEndpointCollection = null; _timeProvider = timeProvider; _lastRefreshTimeStamp = _timeProvider.GetTimestamp(); var cancellation = _lastCollectionCancellation = new CancellationTokenSource(); @@ -58,10 +58,10 @@ protected DnsServiceEndPointResolverBase( protected CancellationToken ShutdownToken => _disposeCancellation.Token; /// - public async ValueTask PopulateAsync(IServiceEndPointBuilder endPoints, CancellationToken cancellationToken) + public async ValueTask PopulateAsync(IServiceEndpointBuilder endpoints, CancellationToken cancellationToken) { // Only add endpoints to the collection if a previous provider (eg, a configuration override) did not add them. - if (endPoints.EndPoints.Count != 0) + if (endpoints.Endpoints.Count != 0) { Log.SkippedResolution(_logger, ServiceName, "Collection has existing endpoints"); return; @@ -85,28 +85,28 @@ public async ValueTask PopulateAsync(IServiceEndPointBuilder endPoints, Cancella lock (_lock) { - if (_lastEndPointCollection is { Count: > 0 } eps) + if (_lastEndpointCollection is { Count: > 0 } eps) { foreach (var ep in eps) { - endPoints.EndPoints.Add(ep); + endpoints.Endpoints.Add(ep); } } - endPoints.AddChangeToken(_lastChangeToken); + endpoints.AddChangeToken(_lastChangeToken); return; } } - private bool ShouldRefresh() => _lastEndPointCollection is null || _lastChangeToken is { HasChanged: true } || ElapsedSinceRefresh >= _nextRefreshPeriod; + private bool ShouldRefresh() => _lastEndpointCollection is null || _lastChangeToken is { HasChanged: true } || ElapsedSinceRefresh >= _nextRefreshPeriod; protected abstract Task ResolveAsyncCore(); - protected void SetResult(List endPoints, TimeSpan validityPeriod) + protected void SetResult(List endpoints, TimeSpan validityPeriod) { lock (_lock) { - if (endPoints is { Count: > 0 }) + if (endpoints is { Count: > 0 }) { _lastRefreshTimeStamp = _timeProvider.GetTimestamp(); _nextRefreshPeriod = DefaultRefreshPeriod; @@ -131,7 +131,7 @@ protected void SetResult(List endPoints, TimeSpan validityPerio _lastCollectionCancellation.Cancel(); var cancellation = _lastCollectionCancellation = new CancellationTokenSource(validityPeriod, _timeProvider); _lastChangeToken = new CancellationChangeToken(cancellation.Token); - _lastEndPointCollection = endPoints; + _lastEndpointCollection = endpoints; } TimeSpan GetRefreshPeriod() diff --git a/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndpointProviderFactory.cs b/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndpointProviderFactory.cs new file mode 100644 index 0000000000..c241ad89dd --- /dev/null +++ b/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndpointProviderFactory.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Microsoft.Extensions.ServiceDiscovery.Dns; + +internal sealed partial class DnsServiceEndpointProviderFactory( + IOptionsMonitor options, + ILogger logger, + TimeProvider timeProvider) : IServiceEndpointProviderFactory +{ + /// + public bool TryCreateProvider(ServiceEndpointQuery query, [NotNullWhen(true)] out IServiceEndpointProvider? provider) + { + provider = new DnsServiceEndpointProvider(query, hostName: query.ServiceName, options, logger, timeProvider); + return true; + } +} diff --git a/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndPointResolverOptions.cs b/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndpointProviderOptions.cs similarity index 84% rename from src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndPointResolverOptions.cs rename to src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndpointProviderOptions.cs index 665c98bbc0..b163afc76f 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndPointResolverOptions.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsServiceEndpointProviderOptions.cs @@ -4,9 +4,9 @@ namespace Microsoft.Extensions.ServiceDiscovery.Dns; /// -/// Options for configuring . +/// Options for configuring . /// -public class DnsServiceEndPointResolverOptions +public class DnsServiceEndpointProviderOptions { /// /// Gets or sets the default refresh period for endpoints resolved from DNS. @@ -31,5 +31,5 @@ public class DnsServiceEndPointResolverOptions /// /// Gets or sets a delegate used to determine whether to apply host name metadata to each resolved endpoint. Defaults to false. /// - public Func ApplyHostNameMetadata { get; set; } = _ => false; + public Func ShouldApplyHostNameMetadata { get; set; } = _ => false; } diff --git a/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsSrvServiceEndPointResolver.cs b/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsSrvServiceEndpointProvider.cs similarity index 71% rename from src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsSrvServiceEndPointResolver.cs rename to src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsSrvServiceEndpointProvider.cs index d59dbfbb69..dd17a7e273 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsSrvServiceEndPointResolver.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsSrvServiceEndpointProvider.cs @@ -9,14 +9,14 @@ namespace Microsoft.Extensions.ServiceDiscovery.Dns; -internal sealed partial class DnsSrvServiceEndPointResolver( - string serviceName, +internal sealed partial class DnsSrvServiceEndpointProvider( + ServiceEndpointQuery query, string srvQuery, string hostName, - IOptionsMonitor options, - ILogger logger, + IOptionsMonitor options, + ILogger logger, IDnsQuery dnsClient, - TimeProvider timeProvider) : DnsServiceEndPointResolverBase(serviceName, logger, timeProvider), IHostNameFeature + TimeProvider timeProvider) : DnsServiceEndpointProviderBase(query, logger, timeProvider), IHostNameFeature { protected override double RetryBackOffFactor => options.CurrentValue.RetryBackOffFactor; @@ -32,7 +32,7 @@ internal sealed partial class DnsSrvServiceEndPointResolver( protected override async Task ResolveAsyncCore() { - var endPoints = new List(); + var endpoints = new List(); var ttl = DefaultRefreshPeriod; Log.SrvQuery(logger, ServiceName, srvQuery); var result = await dnsClient.QueryAsync(srvQuery, QueryType.SRV, cancellationToken: ShutdownToken).ConfigureAwait(false); @@ -59,15 +59,15 @@ protected override async Task ResolveAsyncCore() ttl = MinTtl(record, ttl); if (targetRecord is AddressRecord addressRecord) { - endPoints.Add(CreateEndPoint(new IPEndPoint(addressRecord.Address, record.Port))); + endpoints.Add(CreateEndpoint(new IPEndPoint(addressRecord.Address, record.Port))); } else if (targetRecord is CNameRecord canonicalNameRecord) { - endPoints.Add(CreateEndPoint(new DnsEndPoint(canonicalNameRecord.CanonicalName.Value.TrimEnd('.'), record.Port))); + endpoints.Add(CreateEndpoint(new DnsEndPoint(canonicalNameRecord.CanonicalName.Value.TrimEnd('.'), record.Port))); } } - SetResult(endPoints, ttl); + SetResult(endpoints, ttl); static TimeSpan MinTtl(DnsResourceRecord record, TimeSpan existing) { @@ -79,22 +79,22 @@ InvalidOperationException CreateException(string dnsName, string errorMessage) { var msg = errorMessage switch { - { Length: > 0 } => $"No DNS records were found for service {ServiceName} (DNS name: {dnsName}): {errorMessage}.", - _ => $"No DNS records were found for service {ServiceName} (DNS name: {dnsName})." + { Length: > 0 } => $"No DNS records were found for service '{ServiceName}' (DNS name: '{dnsName}'): {errorMessage}.", + _ => $"No DNS records were found for service '{ServiceName}' (DNS name: '{dnsName}')." }; return new InvalidOperationException(msg); } - ServiceEndPoint CreateEndPoint(EndPoint endPoint) + ServiceEndpoint CreateEndpoint(EndPoint endPoint) { - var serviceEndPoint = ServiceEndPoint.Create(endPoint); - serviceEndPoint.Features.Set(this); - if (options.CurrentValue.ApplyHostNameMetadata(serviceEndPoint)) + var serviceEndpoint = ServiceEndpoint.Create(endPoint); + serviceEndpoint.Features.Set(this); + if (options.CurrentValue.ShouldApplyHostNameMetadata(serviceEndpoint)) { - serviceEndPoint.Features.Set(this); + serviceEndpoint.Features.Set(this); } - return serviceEndPoint; + return serviceEndpoint; } } } diff --git a/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsSrvServiceEndPointResolverProvider.cs b/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsSrvServiceEndpointProviderFactory.cs similarity index 86% rename from src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsSrvServiceEndPointResolverProvider.cs rename to src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsSrvServiceEndpointProviderFactory.cs index 8a75c1d1bb..fd0cb28353 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsSrvServiceEndPointResolverProvider.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsSrvServiceEndpointProviderFactory.cs @@ -8,11 +8,11 @@ namespace Microsoft.Extensions.ServiceDiscovery.Dns; -internal sealed partial class DnsSrvServiceEndPointResolverProvider( - IOptionsMonitor options, - ILogger logger, +internal sealed partial class DnsSrvServiceEndpointProviderFactory( + IOptionsMonitor options, + ILogger logger, IDnsQuery dnsClient, - TimeProvider timeProvider) : IServiceEndPointProviderFactory + TimeProvider timeProvider) : IServiceEndpointProviderFactory { private static readonly string s_serviceAccountPath = Path.Combine($"{Path.DirectorySeparatorChar}var", "run", "secrets", "kubernetes.io", "serviceaccount"); private static readonly string s_serviceAccountNamespacePath = Path.Combine($"{Path.DirectorySeparatorChar}var", "run", "secrets", "kubernetes.io", "serviceaccount", "namespace"); @@ -20,7 +20,7 @@ internal sealed partial class DnsSrvServiceEndPointResolverProvider( private readonly string? _querySuffix = options.CurrentValue.QuerySuffix ?? GetKubernetesHostDomain(); /// - public bool TryCreateProvider(ServiceEndPointQuery query, [NotNullWhen(true)] out IServiceEndPointProvider? resolver) + public bool TryCreateProvider(ServiceEndpointQuery query, [NotNullWhen(true)] out IServiceEndpointProvider? provider) { // If a default namespace is not specified, then this provider will attempt to infer the namespace from the service name, but only when running inside Kubernetes. // Kubernetes DNS spec: https://github.com/kubernetes/dns/blob/master/docs/specification.md @@ -30,17 +30,16 @@ public bool TryCreateProvider(ServiceEndPointQuery query, [NotNullWhen(true)] ou // Otherwise, the namespace can be read from /var/run/secrets/kubernetes.io/serviceaccount/namespace and combined with an assumed suffix of "svc.cluster.local". // The protocol is assumed to be "tcp". // The portName is the name of the port in the service definition. If the serviceName parses as a URI, we use the scheme as the port name, otherwise "default". - var serviceName = query.OriginalString; if (string.IsNullOrWhiteSpace(_querySuffix)) { - DnsServiceEndPointResolverBase.Log.NoDnsSuffixFound(logger, serviceName); - resolver = default; + DnsServiceEndpointProviderBase.Log.NoDnsSuffixFound(logger, query.ToString()!); + provider = default; return false; } - var portName = query.EndPointName ?? "default"; + var portName = query.EndpointName ?? "default"; var srvQuery = $"_{portName}._tcp.{query.ServiceName}.{_querySuffix}"; - resolver = new DnsSrvServiceEndPointResolver(serviceName, srvQuery, hostName: query.ServiceName, options, logger, dnsClient, timeProvider); + provider = new DnsSrvServiceEndpointProvider(query, srvQuery, hostName: query.ServiceName, options, logger, dnsClient, timeProvider); return true; } diff --git a/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsSrvServiceEndPointResolverOptions.cs b/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsSrvServiceEndpointProviderOptions.cs similarity index 86% rename from src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsSrvServiceEndPointResolverOptions.cs rename to src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsSrvServiceEndpointProviderOptions.cs index 704e03cd9c..c908c56d77 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsSrvServiceEndPointResolverOptions.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery.Dns/DnsSrvServiceEndpointProviderOptions.cs @@ -4,9 +4,9 @@ namespace Microsoft.Extensions.ServiceDiscovery.Dns; /// -/// Options for configuring . +/// Options for configuring . /// -public class DnsSrvServiceEndPointResolverOptions +public class DnsSrvServiceEndpointProviderOptions { /// /// Gets or sets the default refresh period for endpoints resolved from DNS. @@ -39,5 +39,5 @@ public class DnsSrvServiceEndPointResolverOptions /// /// Gets or sets a delegate used to determine whether to apply host name metadata to each resolved endpoint. Defaults to false. /// - public Func ApplyHostNameMetadata { get; set; } = _ => false; + public Func ShouldApplyHostNameMetadata { get; set; } = _ => false; } diff --git a/src/Microsoft.Extensions.ServiceDiscovery.Dns/README.md b/src/Microsoft.Extensions.ServiceDiscovery.Dns/README.md index d3fbe2a75e..8be4560870 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery.Dns/README.md +++ b/src/Microsoft.Extensions.ServiceDiscovery.Dns/README.md @@ -1,25 +1,25 @@ # Microsoft.Extensions.ServiceDiscovery.Dns -This library provides support for resolving service endpoints using DNS (Domain Name System). It provides two service endpoint resolvers: +This library provides support for resolving service endpoints using DNS (Domain Name System). It provides two service endpoint providers: -- _DNS_, which resolves endpoints using DNS `A/AAAA` record queries. This means that it can resolve names to IP addresses, but cannot resolve port numbers endpoints. As such, port numbers are assumed to be the default for the protocol (for example, 80 for HTTP and 433 for HTTPS). The benefit of using the DNS resolver is that for cases where these default ports are appropriate, clients can spread their requests across hosts. For more information, see _Load-balancing with endpoint selectors_. +- _DNS_, which resolves endpoints using DNS `A/AAAA` record queries. This means that it can resolve names to IP addresses, but cannot resolve port numbers endpoints. As such, port numbers are assumed to be the default for the protocol (for example, 80 for HTTP and 433 for HTTPS). The benefit of using the DNS provider is that for cases where these default ports are appropriate, clients can spread their requests across hosts. For more information, see _Load-balancing with endpoint selectors_. - _DNS SRV_, which resolves service names using DNS SRV record queries. This allows it to resolve both IP addresses and port numbers. This is useful for environments which support DNS SRV queries, such as Kubernetes (when configured accordingly). ## Resolving service endpoints with DNS -The _DNS_ resolver resolves endpoints using DNS `A/AAAA` record queries. This means that it can resolve names to IP addresses, but cannot resolve port numbers endpoints. As such, port numbers are assumed to be the default for the protocol (for example, 80 for HTTP and 433 for HTTPS). The benefit of using the DNS resolver is that for cases where these default ports are appropriate, clients can spread their requests across hosts. For more information, see _Load-balancing with endpoint selectors_. +The _DNS_ service endpoint provider resolves endpoints using DNS `A/AAAA` record queries. This means that it can resolve names to IP addresses, but cannot resolve port numbers endpoints. As such, port numbers are assumed to be the default for the protocol (for example, 80 for HTTP and 433 for HTTPS). The benefit of using the DNS service endpoint provider is that for cases where these default ports are appropriate, clients can spread their requests across hosts. For more information, see _Load-balancing with endpoint selectors_. -To configure the DNS resolver in your application, add the DNS resolver to your host builder's service collection using the `AddDnsServiceEndPointResolver` method. service discovery as follows: +To configure the DNS service endpoint provider in your application, add the DNS service endpoint provider to your host builder's service collection using the `AddDnsServiceEndpointProvider` method. service discovery as follows: ```csharp builder.Services.AddServiceDiscoveryCore(); -builder.Services.AddDnsServiceEndPointResolver(); +builder.Services.AddDnsServiceEndpointProvider(); ``` ## Resolving service endpoints in Kubernetes with DNS SRV -When deploying to Kubernetes, the DNS SRV service endpoint resolver can be used to resolve endpoints. For example, the following resource definition will result in a DNS SRV record being created for an endpoint named "default" and an endpoint named "dashboard", both on the service named "basket". +When deploying to Kubernetes, the DNS SRV service endpoint provider can be used to resolve endpoints. For example, the following resource definition will result in a DNS SRV record being created for an endpoint named "default" and an endpoint named "dashboard", both on the service named "basket". ```yml apiVersion: v1 @@ -37,11 +37,11 @@ spec: port: 8888 ``` -To configure a service to resolve the "dashboard" endpoint on the "basket" service, add the DNS SRV service endpoint resolver to the host builder as follows: +To configure a service to resolve the "dashboard" endpoint on the "basket" service, add the DNS SRV service endpoint provider to the host builder as follows: ```csharp builder.Services.AddServiceDiscoveryCore(); -builder.Services.AddDnsSrvServiceEndPointResolver(); +builder.Services.AddDnsSrvServiceEndpointProvider(); ``` The special port name "default" is used to specify the default endpoint, resolved using the URI `http://basket`. diff --git a/src/Microsoft.Extensions.ServiceDiscovery.Dns/ServiceDiscoveryDnsServiceCollectionExtensions.cs b/src/Microsoft.Extensions.ServiceDiscovery.Dns/ServiceDiscoveryDnsServiceCollectionExtensions.cs index 0d795660fd..17544d0948 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery.Dns/ServiceDiscoveryDnsServiceCollectionExtensions.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery.Dns/ServiceDiscoveryDnsServiceCollectionExtensions.cs @@ -14,6 +14,17 @@ namespace Microsoft.Extensions.Hosting; /// public static class ServiceDiscoveryDnsServiceCollectionExtensions { + /// + /// Adds DNS SRV service discovery to the . + /// + /// The service collection. + /// The provided . + /// + /// DNS SRV queries are able to provide port numbers for endpoints and can support multiple named endpoints per service. + /// However, not all environment support DNS SRV queries, and in some environments, additional configuration may be required. + /// + public static IServiceCollection AddDnsSrvServiceEndpointProvider(this IServiceCollection services) => services.AddDnsSrvServiceEndpointProvider(_ => { }); + /// /// Adds DNS SRV service discovery to the . /// @@ -24,16 +35,26 @@ public static class ServiceDiscoveryDnsServiceCollectionExtensions /// DNS SRV queries are able to provide port numbers for endpoints and can support multiple named endpoints per service. /// However, not all environment support DNS SRV queries, and in some environments, additional configuration may be required. /// - public static IServiceCollection AddDnsSrvServiceEndPointResolver(this IServiceCollection services, Action? configureOptions = null) + public static IServiceCollection AddDnsSrvServiceEndpointProvider(this IServiceCollection services, Action configureOptions) { services.AddServiceDiscoveryCore(); services.TryAddSingleton(); - services.AddSingleton(); - var options = services.AddOptions(); + services.AddSingleton(); + var options = services.AddOptions(); options.Configure(o => configureOptions?.Invoke(o)); return services; } + /// + /// Adds DNS service discovery to the . + /// + /// The service collection. + /// The provided . + /// + /// DNS A/AAAA queries are widely available but are not able to provide port numbers for endpoints and cannot support multiple named endpoints per service. + /// + public static IServiceCollection AddDnsServiceEndpointProvider(this IServiceCollection services) => services.AddDnsServiceEndpointProvider(_ => { }); + /// /// Adds DNS service discovery to the . /// @@ -43,11 +64,11 @@ public static IServiceCollection AddDnsSrvServiceEndPointResolver(this IServiceC /// /// DNS A/AAAA queries are widely available but are not able to provide port numbers for endpoints and cannot support multiple named endpoints per service. /// - public static IServiceCollection AddDnsServiceEndPointResolver(this IServiceCollection services, Action? configureOptions = null) + public static IServiceCollection AddDnsServiceEndpointProvider(this IServiceCollection services, Action configureOptions) { services.AddServiceDiscoveryCore(); - services.AddSingleton(); - var options = services.AddOptions(); + services.AddSingleton(); + var options = services.AddOptions(); options.Configure(o => configureOptions?.Invoke(o)); return services; } diff --git a/src/Microsoft.Extensions.ServiceDiscovery.Yarp/ServiceDiscoveryDestinationResolver.cs b/src/Microsoft.Extensions.ServiceDiscovery.Yarp/ServiceDiscoveryDestinationResolver.cs index 22d7e6d832..8ec810f2c0 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery.Yarp/ServiceDiscoveryDestinationResolver.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery.Yarp/ServiceDiscoveryDestinationResolver.cs @@ -14,7 +14,7 @@ namespace Microsoft.Extensions.ServiceDiscovery.Yarp; /// Initializes a new instance. /// /// The endpoint resolver registry. -internal sealed class ServiceDiscoveryDestinationResolver(ServiceEndPointResolver resolver) : IDestinationResolver +internal sealed class ServiceDiscoveryDestinationResolver(ServiceEndpointResolver resolver) : IDestinationResolver { /// public async ValueTask ResolveDestinationsAsync(IReadOnlyDictionary destinations, CancellationToken cancellationToken) @@ -54,14 +54,14 @@ public async ValueTask ResolveDestinationsAsync(I var originalHost = originalConfig.Host is { Length: > 0 } h ? h : originalUri.Authority; var serviceName = originalUri.GetLeftPart(UriPartial.Authority); - var result = await resolver.GetEndPointsAsync(serviceName, cancellationToken).ConfigureAwait(false); - var results = new List<(string Name, DestinationConfig Config)>(result.EndPoints.Count); + var result = await resolver.GetEndpointsAsync(serviceName, cancellationToken).ConfigureAwait(false); + var results = new List<(string Name, DestinationConfig Config)>(result.Endpoints.Count); var uriBuilder = new UriBuilder(originalUri); var healthUri = originalConfig.Health is { Length: > 0 } health ? new Uri(health) : null; var healthUriBuilder = healthUri is { } ? new UriBuilder(healthUri) : null; - foreach (var endPoint in result.EndPoints) + foreach (var endpoint in result.Endpoints) { - var addressString = endPoint.GetEndPointString(); + var addressString = endpoint.ToString()!; Uri uri; if (!addressString.Contains("://")) { diff --git a/src/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndPointResolver.Log.cs b/src/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndpointProvider.Log.cs similarity index 78% rename from src/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndPointResolver.Log.cs rename to src/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndpointProvider.Log.cs index fdb61ef59f..48c922a85b 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndPointResolver.Log.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndpointProvider.Log.cs @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.ServiceDiscovery.Configuration; -internal sealed partial class ConfigurationServiceEndPointResolver +internal sealed partial class ConfigurationServiceEndpointProvider { private sealed partial class Log { @@ -19,10 +19,10 @@ private sealed partial class Log [LoggerMessage(3, LogLevel.Debug, "No valid endpoint configuration was found for service '{ServiceName}' from path '{Path}'.", EventName = "ServiceConfigurationNotFound")] internal static partial void ServiceConfigurationNotFound(ILogger logger, string serviceName, string path); - [LoggerMessage(4, LogLevel.Debug, "Endpoints configured for service '{ServiceName}' from path '{Path}': {ConfiguredEndPoints}.", EventName = "ConfiguredEndPoints")] - internal static partial void ConfiguredEndPoints(ILogger logger, string serviceName, string path, string configuredEndPoints); + [LoggerMessage(4, LogLevel.Debug, "Endpoints configured for service '{ServiceName}' from path '{Path}': {ConfiguredEndpoints}.", EventName = "ConfiguredEndpoints")] + internal static partial void ConfiguredEndpoints(ILogger logger, string serviceName, string path, string configuredEndpoints); - internal static void ConfiguredEndPoints(ILogger logger, string serviceName, string path, IList endpoints, int added) + internal static void ConfiguredEndpoints(ILogger logger, string serviceName, string path, IList endpoints, int added) { if (!logger.IsEnabled(LogLevel.Debug)) { @@ -40,8 +40,8 @@ internal static void ConfiguredEndPoints(ILogger logger, string serviceName, str endpointValues.Append(endpoints[i].ToString()); } - var configuredEndPoints = endpointValues.ToString(); - ConfiguredEndPoints(logger, serviceName, path, configuredEndPoints); + var configuredEndpoints = endpointValues.ToString(); + ConfiguredEndpoints(logger, serviceName, path, configuredEndpoints); } [LoggerMessage(5, LogLevel.Debug, "No valid endpoint configuration was found for endpoint '{EndpointName}' on service '{ServiceName}' from path '{Path}'.", EventName = "EndpointConfigurationNotFound")] diff --git a/src/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndPointResolver.cs b/src/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndpointProvider.cs similarity index 76% rename from src/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndPointResolver.cs rename to src/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndpointProvider.cs index 9604ec0c20..37078e0196 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndPointResolver.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndpointProvider.cs @@ -10,36 +10,36 @@ namespace Microsoft.Extensions.ServiceDiscovery.Configuration; /// -/// A service endpoint resolver that uses configuration to resolve resolved. +/// A service endpoint provider that uses configuration to resolve resolved. /// -internal sealed partial class ConfigurationServiceEndPointResolver : IServiceEndPointProvider, IHostNameFeature +internal sealed partial class ConfigurationServiceEndpointProvider : IServiceEndpointProvider, IHostNameFeature { - private const string DefaultEndPointName = "default"; + private const string DefaultEndpointName = "default"; private readonly string _serviceName; private readonly string? _endpointName; private readonly string[] _schemes; private readonly IConfiguration _configuration; - private readonly ILogger _logger; - private readonly IOptions _options; + private readonly ILogger _logger; + private readonly IOptions _options; /// - /// Initializes a new instance. + /// Initializes a new instance. /// /// The query. /// The configuration. /// The logger. - /// Configuration resolver options. + /// Configuration provider options. /// Service discovery options. - public ConfigurationServiceEndPointResolver( - ServiceEndPointQuery query, + public ConfigurationServiceEndpointProvider( + ServiceEndpointQuery query, IConfiguration configuration, - ILogger logger, - IOptions options, + ILogger logger, + IOptions options, IOptions serviceDiscoveryOptions) { _serviceName = query.ServiceName; - _endpointName = query.EndPointName; - _schemes = ServiceDiscoveryOptions.ApplyAllowedSchemes(query.IncludeSchemes, serviceDiscoveryOptions.Value.AllowedSchemes); + _endpointName = query.EndpointName; + _schemes = ServiceDiscoveryOptions.ApplyAllowedSchemes(query.IncludedSchemes, serviceDiscoveryOptions.Value.AllowedSchemes, serviceDiscoveryOptions.Value.AllowAllSchemes); _configuration = configuration; _logger = logger; _options = options; @@ -49,10 +49,10 @@ public ConfigurationServiceEndPointResolver( public ValueTask DisposeAsync() => default; /// - public ValueTask PopulateAsync(IServiceEndPointBuilder endPoints, CancellationToken cancellationToken) + public ValueTask PopulateAsync(IServiceEndpointBuilder endpoints, CancellationToken cancellationToken) { // Only add resolved to the collection if a previous provider (eg, an override) did not add them. - if (endPoints.EndPoints.Count != 0) + if (endpoints.Endpoints.Count != 0) { Log.SkippedResolution(_logger, _serviceName, "Collection has existing endpoints"); return default; @@ -62,12 +62,12 @@ public ValueTask PopulateAsync(IServiceEndPointBuilder endPoints, CancellationTo var section = _configuration.GetSection(_options.Value.SectionName).GetSection(_serviceName); if (!section.Exists()) { - endPoints.AddChangeToken(_configuration.GetReloadToken()); + endpoints.AddChangeToken(_configuration.GetReloadToken()); Log.ServiceConfigurationNotFound(_logger, _serviceName, $"{_options.Value.SectionName}:{_serviceName}"); return default; } - endPoints.AddChangeToken(section.GetReloadToken()); + endpoints.AddChangeToken(section.GetReloadToken()); // Find an appropriate configuration section based on the input. IConfigurationSection? namedSection = null; @@ -77,7 +77,7 @@ public ValueTask PopulateAsync(IServiceEndPointBuilder endPoints, CancellationTo if (_schemes.Length == 0) { // Use the section named "default". - endpointName = DefaultEndPointName; + endpointName = DefaultEndpointName; namedSection = section.GetSection(endpointName); } else @@ -112,14 +112,14 @@ public ValueTask PopulateAsync(IServiceEndPointBuilder endPoints, CancellationTo return default; } - List resolved = []; + List resolved = []; Log.UsingConfigurationPath(_logger, configPath, endpointName, _serviceName); // Account for both the single and multi-value cases. if (!string.IsNullOrWhiteSpace(namedSection.Value)) { // Single value case. - AddEndPoint(resolved, namedSection, endpointName); + AddEndpoint(resolved, namedSection, endpointName); } else { @@ -131,7 +131,7 @@ public ValueTask PopulateAsync(IServiceEndPointBuilder endPoints, CancellationTo throw new KeyNotFoundException($"The endpoint configuration section for service '{_serviceName}' endpoint '{endpointName}' has non-numeric keys."); } - AddEndPoint(resolved, child, endpointName); + AddEndpoint(resolved, child, endpointName); } } @@ -158,13 +158,13 @@ public ValueTask PopulateAsync(IServiceEndPointBuilder endPoints, CancellationTo if (index >= 0 && index <= minIndex) { ++added; - endPoints.EndPoints.Add(ep); + endpoints.Endpoints.Add(ep); } } else { ++added; - endPoints.EndPoints.Add(ep); + endpoints.Endpoints.Add(ep); } } @@ -174,7 +174,7 @@ public ValueTask PopulateAsync(IServiceEndPointBuilder endPoints, CancellationTo } else { - Log.ConfiguredEndPoints(_logger, _serviceName, configPath, endPoints.EndPoints, added); + Log.ConfiguredEndpoints(_logger, _serviceName, configPath, endpoints.Endpoints, added); } return default; @@ -182,7 +182,7 @@ public ValueTask PopulateAsync(IServiceEndPointBuilder endPoints, CancellationTo string IHostNameFeature.HostName => _serviceName; - private void AddEndPoint(List endPoints, IConfigurationSection section, string endpointName) + private void AddEndpoint(List endpoints, IConfigurationSection section, string endpointName) { var value = section.Value; if (string.IsNullOrWhiteSpace(value) || !TryParseEndPoint(value, out var endPoint)) @@ -190,7 +190,7 @@ private void AddEndPoint(List endPoints, IConfigurationSection throw new KeyNotFoundException($"The endpoint configuration section for service '{_serviceName}' endpoint '{endpointName}' has an invalid value with key '{section.Key}'."); } - endPoints.Add(CreateEndPoint(endPoint)); + endpoints.Add(CreateEndpoint(endPoint)); } private static bool TryParseEndPoint(string value, [NotNullWhen(true)] out EndPoint? endPoint) @@ -220,16 +220,16 @@ private static bool TryParseEndPoint(string value, [NotNullWhen(true)] out EndPo return true; } - private ServiceEndPoint CreateEndPoint(EndPoint endPoint) + private ServiceEndpoint CreateEndpoint(EndPoint endPoint) { - var serviceEndPoint = ServiceEndPoint.Create(endPoint); - serviceEndPoint.Features.Set(this); - if (_options.Value.ApplyHostNameMetadata(serviceEndPoint)) + var serviceEndpoint = ServiceEndpoint.Create(endPoint); + serviceEndpoint.Features.Set(this); + if (_options.Value.ShouldApplyHostNameMetadata(serviceEndpoint)) { - serviceEndPoint.Features.Set(this); + serviceEndpoint.Features.Set(this); } - return serviceEndPoint; + return serviceEndpoint; } public override string ToString() => "Configuration"; diff --git a/src/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndPointResolverProvider.cs b/src/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndpointProviderFactory.cs similarity index 58% rename from src/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndPointResolverProvider.cs rename to src/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndpointProviderFactory.cs index 032c50b6f2..a966cd4479 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndPointResolverProvider.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndpointProviderFactory.cs @@ -9,18 +9,18 @@ namespace Microsoft.Extensions.ServiceDiscovery.Configuration; /// -/// implementation that resolves services using . +/// implementation that resolves services using . /// -internal sealed class ConfigurationServiceEndPointResolverProvider( +internal sealed class ConfigurationServiceEndpointProviderFactory( IConfiguration configuration, - IOptions options, + IOptions options, IOptions serviceDiscoveryOptions, - ILogger logger) : IServiceEndPointProviderFactory + ILogger logger) : IServiceEndpointProviderFactory { /// - public bool TryCreateProvider(ServiceEndPointQuery query, [NotNullWhen(true)] out IServiceEndPointProvider? resolver) + public bool TryCreateProvider(ServiceEndpointQuery query, [NotNullWhen(true)] out IServiceEndpointProvider? provider) { - resolver = new ConfigurationServiceEndPointResolver(query, configuration, logger, options, serviceDiscoveryOptions); + provider = new ConfigurationServiceEndpointProvider(query, configuration, logger, options, serviceDiscoveryOptions); return true; } } diff --git a/src/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndPointResolverOptionsValidator.cs b/src/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndpointProviderOptionsValidator.cs similarity index 62% rename from src/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndPointResolverOptionsValidator.cs rename to src/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndpointProviderOptionsValidator.cs index 91e97b5d0b..f8092c4dd5 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndPointResolverOptionsValidator.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndpointProviderOptionsValidator.cs @@ -1,22 +1,22 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Extensions.Options; namespace Microsoft.Extensions.ServiceDiscovery.Configuration; -internal sealed class ConfigurationServiceEndPointResolverOptionsValidator : IValidateOptions +internal sealed class ConfigurationServiceEndpointProviderOptionsValidator : IValidateOptions { - public ValidateOptionsResult Validate(string? name, ConfigurationServiceEndPointResolverOptions options) + public ValidateOptionsResult Validate(string? name, ConfigurationServiceEndpointProviderOptions options) { if (string.IsNullOrWhiteSpace(options.SectionName)) { return ValidateOptionsResult.Fail($"{nameof(options.SectionName)} must not be null or empty."); } - if (options.ApplyHostNameMetadata is null) + if (options.ShouldApplyHostNameMetadata is null) { - return ValidateOptionsResult.Fail($"{nameof(options.ApplyHostNameMetadata)} must not be null."); + return ValidateOptionsResult.Fail($"{nameof(options.ShouldApplyHostNameMetadata)} must not be null."); } return ValidateOptionsResult.Success; diff --git a/src/Microsoft.Extensions.ServiceDiscovery/ConfigurationServiceEndPointResolverOptions.cs b/src/Microsoft.Extensions.ServiceDiscovery/ConfigurationServiceEndpointProviderOptions.cs similarity index 75% rename from src/Microsoft.Extensions.ServiceDiscovery/ConfigurationServiceEndPointResolverOptions.cs rename to src/Microsoft.Extensions.ServiceDiscovery/ConfigurationServiceEndpointProviderOptions.cs index d3b94f2f1e..29f28e359f 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery/ConfigurationServiceEndPointResolverOptions.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery/ConfigurationServiceEndpointProviderOptions.cs @@ -6,9 +6,9 @@ namespace Microsoft.Extensions.ServiceDiscovery; /// -/// Options for . +/// Options for . /// -public sealed class ConfigurationServiceEndPointResolverOptions +public sealed class ConfigurationServiceEndpointProviderOptions { /// /// The name of the configuration section which contains service endpoints. Defaults to "Services". @@ -18,5 +18,5 @@ public sealed class ConfigurationServiceEndPointResolverOptions /// /// Gets or sets a delegate used to determine whether to apply host name metadata to each resolved endpoint. Defaults to a delegate which returns false. /// - public Func ApplyHostNameMetadata { get; set; } = _ => false; + public Func ShouldApplyHostNameMetadata { get; set; } = _ => false; } diff --git a/src/Microsoft.Extensions.ServiceDiscovery/Http/HttpServiceEndPointResolver.cs b/src/Microsoft.Extensions.ServiceDiscovery/Http/HttpServiceEndpointResolver.cs similarity index 82% rename from src/Microsoft.Extensions.ServiceDiscovery/Http/HttpServiceEndPointResolver.cs rename to src/Microsoft.Extensions.ServiceDiscovery/Http/HttpServiceEndpointResolver.cs index 44e58b0dbb..e11f593776 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery/Http/HttpServiceEndPointResolver.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery/Http/HttpServiceEndpointResolver.cs @@ -11,13 +11,13 @@ namespace Microsoft.Extensions.ServiceDiscovery.Http; /// /// Resolves endpoints for HTTP requests. /// -internal sealed class HttpServiceEndPointResolver(ServiceEndPointWatcherFactory resolverFactory, IServiceProvider serviceProvider, TimeProvider timeProvider) : IAsyncDisposable +internal sealed class HttpServiceEndpointResolver(ServiceEndpointWatcherFactory watcherFactory, IServiceProvider serviceProvider, TimeProvider timeProvider) : IAsyncDisposable { - private static readonly TimerCallback s_cleanupCallback = s => ((HttpServiceEndPointResolver)s!).CleanupResolvers(); + private static readonly TimerCallback s_cleanupCallback = s => ((HttpServiceEndpointResolver)s!).CleanupResolvers(); private static readonly TimeSpan s_cleanupPeriod = TimeSpan.FromSeconds(10); private readonly object _lock = new(); - private readonly ServiceEndPointWatcherFactory _resolverFactory = resolverFactory; + private readonly ServiceEndpointWatcherFactory _watcherFactory = watcherFactory; private readonly ConcurrentDictionary _resolvers = new(); private ITimer? _cleanupTimer; private Task? _cleanupTask; @@ -29,7 +29,7 @@ internal sealed class HttpServiceEndPointResolver(ServiceEndPointWatcherFactory /// A . /// The resolved service endpoint. /// The request had no set or a suitable endpoint could not be found. - public async ValueTask GetEndpointAsync(HttpRequestMessage request, CancellationToken cancellationToken) + public async ValueTask GetEndpointAsync(HttpRequestMessage request, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(request); if (request.RequestUri is null) @@ -47,15 +47,15 @@ public async ValueTask GetEndpointAsync(HttpRequestMessage requ static (name, self) => self.CreateResolver(name), this); - var (valid, endPoint) = await resolver.TryGetEndPointAsync(request, cancellationToken).ConfigureAwait(false); + var (valid, endpoint) = await resolver.TryGetEndpointAsync(request, cancellationToken).ConfigureAwait(false); if (valid) { - if (endPoint is null) + if (endpoint is null) { throw new InvalidOperationException($"Unable to resolve endpoint for service {resolver.ServiceName}"); } - return endPoint; + return endpoint; } } } @@ -148,37 +148,37 @@ private async Task CleanupResolversAsyncCore() private ResolverEntry CreateResolver(string serviceName) { - var resolver = _resolverFactory.CreateWatcher(serviceName); - var selector = serviceProvider.GetService() ?? new RoundRobinServiceEndPointSelector(); - var result = new ResolverEntry(resolver, selector); - resolver.Start(); + var watcher = _watcherFactory.CreateWatcher(serviceName); + var selector = serviceProvider.GetService() ?? new RoundRobinServiceEndpointSelector(); + var result = new ResolverEntry(watcher, selector); + watcher.Start(); return result; } private sealed class ResolverEntry : IAsyncDisposable { - private readonly ServiceEndPointWatcher _resolver; - private readonly IServiceEndPointSelector _selector; + private readonly ServiceEndpointWatcher _watcher; + private readonly IServiceEndpointSelector _selector; private const ulong CountMask = ~(RecentUseFlag | DisposingFlag); private const ulong RecentUseFlag = 1UL << 62; private const ulong DisposingFlag = 1UL << 63; private ulong _status; private TaskCompletionSource? _onDisposed; - public ResolverEntry(ServiceEndPointWatcher resolver, IServiceEndPointSelector selector) + public ResolverEntry(ServiceEndpointWatcher watcher, IServiceEndpointSelector selector) { - _resolver = resolver; + _watcher = watcher; _selector = selector; - _resolver.OnEndPointsUpdated += result => + _watcher.OnEndpointsUpdated += result => { if (result.ResolvedSuccessfully) { - _selector.SetEndPoints(result.EndPointSource); + _selector.SetEndpoints(result.EndpointSource); } }; } - public string ServiceName => _resolver.ServiceName; + public string ServiceName => _watcher.ServiceName; public bool CanExpire() { @@ -189,17 +189,17 @@ public bool CanExpire() return (status & (CountMask | RecentUseFlag)) == 0; } - public async ValueTask<(bool Valid, ServiceEndPoint? EndPoint)> TryGetEndPointAsync(object? context, CancellationToken cancellationToken) + public async ValueTask<(bool Valid, ServiceEndpoint? Endpoint)> TryGetEndpointAsync(object? context, CancellationToken cancellationToken) { try { var status = Interlocked.Increment(ref _status); if ((status & DisposingFlag) == 0) { - // If the resolver is valid, resolve. + // If the watcher is valid, resolve. // We ensure that it will not be disposed while we are resolving. - await _resolver.GetEndPointsAsync(cancellationToken).ConfigureAwait(false); - var result = _selector.GetEndPoint(context); + await _watcher.GetEndpointsAsync(cancellationToken).ConfigureAwait(false); + var result = _selector.GetEndpoint(context); return (true, result); } else @@ -246,7 +246,7 @@ private async Task DisposeAsyncCore() { try { - await _resolver.DisposeAsync().ConfigureAwait(false); + await _watcher.DisposeAsync().ConfigureAwait(false); } finally { diff --git a/src/Microsoft.Extensions.ServiceDiscovery/Http/IServiceDiscoveryHttpMessageHandlerFactory.cs b/src/Microsoft.Extensions.ServiceDiscovery/Http/IServiceDiscoveryHttpMessageHandlerFactory.cs index 0febfa9481..0c5bd02d10 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery/Http/IServiceDiscoveryHttpMessageHandlerFactory.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery/Http/IServiceDiscoveryHttpMessageHandlerFactory.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. namespace Microsoft.Extensions.ServiceDiscovery.Http; diff --git a/src/Microsoft.Extensions.ServiceDiscovery/Http/ResolvingHttpClientHandler.cs b/src/Microsoft.Extensions.ServiceDiscovery/Http/ResolvingHttpClientHandler.cs index bc06a03170..a0063ae476 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery/Http/ResolvingHttpClientHandler.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery/Http/ResolvingHttpClientHandler.cs @@ -8,9 +8,9 @@ namespace Microsoft.Extensions.ServiceDiscovery.Http; /// /// which resolves endpoints using service discovery. /// -internal sealed class ResolvingHttpClientHandler(HttpServiceEndPointResolver resolver, IOptions options) : HttpClientHandler +internal sealed class ResolvingHttpClientHandler(HttpServiceEndpointResolver resolver, IOptions options) : HttpClientHandler { - private readonly HttpServiceEndPointResolver _resolver = resolver; + private readonly HttpServiceEndpointResolver _resolver = resolver; private readonly ServiceDiscoveryOptions _options = options.Value; /// @@ -23,7 +23,7 @@ protected override async Task SendAsync(HttpRequestMessage if (originalUri?.Host is not null) { var result = await _resolver.GetEndpointAsync(request, cancellationToken).ConfigureAwait(false); - request.RequestUri = ResolvingHttpDelegatingHandler.GetUriWithEndPoint(originalUri, result, _options); + request.RequestUri = ResolvingHttpDelegatingHandler.GetUriWithEndpoint(originalUri, result, _options); request.Headers.Host ??= result.Features.Get()?.HostName; } diff --git a/src/Microsoft.Extensions.ServiceDiscovery/Http/ResolvingHttpDelegatingHandler.cs b/src/Microsoft.Extensions.ServiceDiscovery/Http/ResolvingHttpDelegatingHandler.cs index daa7b8a17d..8f13bb60ab 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery/Http/ResolvingHttpDelegatingHandler.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery/Http/ResolvingHttpDelegatingHandler.cs @@ -11,7 +11,7 @@ namespace Microsoft.Extensions.ServiceDiscovery.Http; /// internal sealed class ResolvingHttpDelegatingHandler : DelegatingHandler { - private readonly HttpServiceEndPointResolver _resolver; + private readonly HttpServiceEndpointResolver _resolver; private readonly ServiceDiscoveryOptions _options; /// @@ -19,7 +19,7 @@ internal sealed class ResolvingHttpDelegatingHandler : DelegatingHandler /// /// The endpoint resolver. /// The service discovery options. - public ResolvingHttpDelegatingHandler(HttpServiceEndPointResolver resolver, IOptions options) + public ResolvingHttpDelegatingHandler(HttpServiceEndpointResolver resolver, IOptions options) { _resolver = resolver; _options = options.Value; @@ -31,7 +31,7 @@ public ResolvingHttpDelegatingHandler(HttpServiceEndPointResolver resolver, IOpt /// The endpoint resolver. /// The service discovery options. /// The inner handler. - public ResolvingHttpDelegatingHandler(HttpServiceEndPointResolver resolver, IOptions options, HttpMessageHandler innerHandler) : base(innerHandler) + public ResolvingHttpDelegatingHandler(HttpServiceEndpointResolver resolver, IOptions options, HttpMessageHandler innerHandler) : base(innerHandler) { _resolver = resolver; _options = options.Value; @@ -44,7 +44,7 @@ protected override async Task SendAsync(HttpRequestMessage if (originalUri?.Host is not null) { var result = await _resolver.GetEndpointAsync(request, cancellationToken).ConfigureAwait(false); - request.RequestUri = GetUriWithEndPoint(originalUri, result, _options); + request.RequestUri = GetUriWithEndpoint(originalUri, result, _options); request.Headers.Host ??= result.Features.Get()?.HostName; } @@ -58,11 +58,11 @@ protected override async Task SendAsync(HttpRequestMessage } } - internal static Uri GetUriWithEndPoint(Uri uri, ServiceEndPoint serviceEndPoint, ServiceDiscoveryOptions options) + internal static Uri GetUriWithEndpoint(Uri uri, ServiceEndpoint serviceEndpoint, ServiceDiscoveryOptions options) { - var endpoint = serviceEndPoint.EndPoint; + var endPoint = serviceEndpoint.EndPoint; UriBuilder result; - if (endpoint is UriEndPoint { Uri: { } ep }) + if (endPoint is UriEndPoint { Uri: { } ep }) { result = new UriBuilder(uri) { @@ -84,7 +84,7 @@ internal static Uri GetUriWithEndPoint(Uri uri, ServiceEndPoint serviceEndPoint, { string host; int port; - switch (endpoint) + switch (endPoint) { case IPEndPoint ip: host = ip.Address.ToString(); @@ -95,7 +95,7 @@ internal static Uri GetUriWithEndPoint(Uri uri, ServiceEndPoint serviceEndPoint, port = dns.Port; break; default: - throw new InvalidOperationException($"Endpoints of type {endpoint.GetType()} are not supported"); + throw new InvalidOperationException($"Endpoints of type {endPoint.GetType()} are not supported"); } result = new UriBuilder(uri) @@ -112,7 +112,7 @@ internal static Uri GetUriWithEndPoint(Uri uri, ServiceEndPoint serviceEndPoint, if (uri.Scheme.IndexOf('+') > 0) { var scheme = uri.Scheme.Split('+')[0]; - if (options.AllowedSchemes.Equals(ServiceDiscoveryOptions.AllowAllSchemes) || options.AllowedSchemes.Contains(scheme, StringComparer.OrdinalIgnoreCase)) + if (options.AllowAllSchemes || options.AllowedSchemes.Contains(scheme, StringComparer.OrdinalIgnoreCase)) { result.Scheme = scheme; } diff --git a/src/Microsoft.Extensions.ServiceDiscovery/Http/ServiceDiscoveryHttpMessageHandlerFactory.cs b/src/Microsoft.Extensions.ServiceDiscovery/Http/ServiceDiscoveryHttpMessageHandlerFactory.cs index 0d3ba00122..e5e7f7587b 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery/Http/ServiceDiscoveryHttpMessageHandlerFactory.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery/Http/ServiceDiscoveryHttpMessageHandlerFactory.cs @@ -8,12 +8,12 @@ namespace Microsoft.Extensions.ServiceDiscovery.Http; internal sealed class ServiceDiscoveryHttpMessageHandlerFactory( TimeProvider timeProvider, IServiceProvider serviceProvider, - ServiceEndPointWatcherFactory factory, + ServiceEndpointWatcherFactory factory, IOptions options) : IServiceDiscoveryHttpMessageHandlerFactory { public HttpMessageHandler CreateHandler(HttpMessageHandler handler) { - var registry = new HttpServiceEndPointResolver(factory, serviceProvider, timeProvider); + var registry = new HttpServiceEndpointResolver(factory, serviceProvider, timeProvider); return new ResolvingHttpDelegatingHandler(registry, options, handler); } } diff --git a/src/Microsoft.Extensions.ServiceDiscovery/Internal/ServiceEndPointResolverResult.cs b/src/Microsoft.Extensions.ServiceDiscovery/Internal/ServiceEndpointResolverResult.cs similarity index 73% rename from src/Microsoft.Extensions.ServiceDiscovery/Internal/ServiceEndPointResolverResult.cs rename to src/Microsoft.Extensions.ServiceDiscovery/Internal/ServiceEndpointResolverResult.cs index 07bffa5654..675941bb95 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery/Internal/ServiceEndPointResolverResult.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery/Internal/ServiceEndpointResolverResult.cs @@ -8,9 +8,9 @@ namespace Microsoft.Extensions.ServiceDiscovery.Internal; /// /// Represents the result of service endpoint resolution. /// -/// The endpoint collection. +/// The endpoint collection. /// The exception which occurred during resolution. -internal sealed class ServiceEndPointResolverResult(ServiceEndPointSource? endPointSource, Exception? exception) +internal sealed class ServiceEndpointResolverResult(ServiceEndpointSource? endpointSource, Exception? exception) { /// /// Gets the exception which occurred during resolution. @@ -20,11 +20,11 @@ internal sealed class ServiceEndPointResolverResult(ServiceEndPointSource? endPo /// /// Gets a value indicating whether resolution completed successfully. /// - [MemberNotNullWhen(true, nameof(EndPointSource))] + [MemberNotNullWhen(true, nameof(EndpointSource))] public bool ResolvedSuccessfully => Exception is null; /// /// Gets the endpoints. /// - public ServiceEndPointSource? EndPointSource { get; } = endPointSource; + public ServiceEndpointSource? EndpointSource { get; } = endpointSource; } diff --git a/src/Microsoft.Extensions.ServiceDiscovery/LoadBalancing/IServiceEndPointSelector.cs b/src/Microsoft.Extensions.ServiceDiscovery/LoadBalancing/IServiceEndpointSelector.cs similarity index 69% rename from src/Microsoft.Extensions.ServiceDiscovery/LoadBalancing/IServiceEndPointSelector.cs rename to src/Microsoft.Extensions.ServiceDiscovery/LoadBalancing/IServiceEndpointSelector.cs index bd0172c45c..2d81ff3860 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery/LoadBalancing/IServiceEndPointSelector.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery/LoadBalancing/IServiceEndpointSelector.cs @@ -6,18 +6,18 @@ namespace Microsoft.Extensions.ServiceDiscovery.LoadBalancing; /// /// Selects endpoints from a collection of endpoints. /// -internal interface IServiceEndPointSelector +internal interface IServiceEndpointSelector { /// /// Sets the collection of endpoints which this instance will select from. /// - /// The collection of endpoints to select from. - void SetEndPoints(ServiceEndPointSource endPoints); + /// The collection of endpoints to select from. + void SetEndpoints(ServiceEndpointSource endpoints); /// - /// Selects an endpoints from the collection provided by the most recent call to . + /// Selects an endpoints from the collection provided by the most recent call to . /// /// The context. /// An endpoint. - ServiceEndPoint GetEndPoint(object? context); + ServiceEndpoint GetEndpoint(object? context); } diff --git a/src/Microsoft.Extensions.ServiceDiscovery/LoadBalancing/RoundRobinServiceEndPointSelector.cs b/src/Microsoft.Extensions.ServiceDiscovery/LoadBalancing/RoundRobinServiceEndpointSelector.cs similarity index 63% rename from src/Microsoft.Extensions.ServiceDiscovery/LoadBalancing/RoundRobinServiceEndPointSelector.cs rename to src/Microsoft.Extensions.ServiceDiscovery/LoadBalancing/RoundRobinServiceEndpointSelector.cs index 92da7cf25b..e7e51bc602 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery/LoadBalancing/RoundRobinServiceEndPointSelector.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery/LoadBalancing/RoundRobinServiceEndpointSelector.cs @@ -6,21 +6,21 @@ namespace Microsoft.Extensions.ServiceDiscovery.LoadBalancing; /// /// Selects endpoints by iterating through the list of endpoints in a round-robin fashion. /// -internal sealed class RoundRobinServiceEndPointSelector : IServiceEndPointSelector +internal sealed class RoundRobinServiceEndpointSelector : IServiceEndpointSelector { private uint _next; - private IReadOnlyList? _endPoints; + private IReadOnlyList? _endpoints; /// - public void SetEndPoints(ServiceEndPointSource endPoints) + public void SetEndpoints(ServiceEndpointSource endpoints) { - _endPoints = endPoints.EndPoints; + _endpoints = endpoints.Endpoints; } /// - public ServiceEndPoint GetEndPoint(object? context) + public ServiceEndpoint GetEndpoint(object? context) { - if (_endPoints is not { Count: > 0 } collection) + if (_endpoints is not { Count: > 0 } collection) { throw new InvalidOperationException("The endpoint collection contains no endpoints"); } diff --git a/src/Microsoft.Extensions.ServiceDiscovery/PassThrough/PassThroughServiceEndPointResolver.Log.cs b/src/Microsoft.Extensions.ServiceDiscovery/PassThrough/PassThroughServiceEndpointProvider.Log.cs similarity index 78% rename from src/Microsoft.Extensions.ServiceDiscovery/PassThrough/PassThroughServiceEndPointResolver.Log.cs rename to src/Microsoft.Extensions.ServiceDiscovery/PassThrough/PassThroughServiceEndpointProvider.Log.cs index 570eb5e4e4..f9a984cfe4 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery/PassThrough/PassThroughServiceEndPointResolver.Log.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery/PassThrough/PassThroughServiceEndpointProvider.Log.cs @@ -5,11 +5,11 @@ namespace Microsoft.Extensions.ServiceDiscovery.PassThrough; -internal sealed partial class PassThroughServiceEndPointResolver +internal sealed partial class PassThroughServiceEndpointProvider { private sealed partial class Log { - [LoggerMessage(1, LogLevel.Debug, "Using pass-through service endpoint resolver for service '{ServiceName}'.", EventName = "UsingPassThrough")] + [LoggerMessage(1, LogLevel.Debug, "Using pass-through service endpoint provider for service '{ServiceName}'.", EventName = "UsingPassThrough")] internal static partial void UsingPassThrough(ILogger logger, string serviceName); } } diff --git a/src/Microsoft.Extensions.ServiceDiscovery/PassThrough/PassThroughServiceEndPointResolver.cs b/src/Microsoft.Extensions.ServiceDiscovery/PassThrough/PassThroughServiceEndpointProvider.cs similarity index 60% rename from src/Microsoft.Extensions.ServiceDiscovery/PassThrough/PassThroughServiceEndPointResolver.cs rename to src/Microsoft.Extensions.ServiceDiscovery/PassThrough/PassThroughServiceEndpointProvider.cs index 483c08702d..478d81d42d 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery/PassThrough/PassThroughServiceEndPointResolver.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery/PassThrough/PassThroughServiceEndpointProvider.cs @@ -7,18 +7,18 @@ namespace Microsoft.Extensions.ServiceDiscovery.PassThrough; /// -/// Service endpoint resolver which passes through the provided value. +/// Service endpoint provider which passes through the provided value. /// -internal sealed partial class PassThroughServiceEndPointResolver(ILogger logger, string serviceName, EndPoint endPoint) : IServiceEndPointProvider +internal sealed partial class PassThroughServiceEndpointProvider(ILogger logger, string serviceName, EndPoint endPoint) : IServiceEndpointProvider { - public ValueTask PopulateAsync(IServiceEndPointBuilder endPoints, CancellationToken cancellationToken) + public ValueTask PopulateAsync(IServiceEndpointBuilder endpoints, CancellationToken cancellationToken) { - if (endPoints.EndPoints.Count == 0) + if (endpoints.Endpoints.Count == 0) { Log.UsingPassThrough(logger, serviceName); - var ep = ServiceEndPoint.Create(endPoint); - ep.Features.Set(this); - endPoints.EndPoints.Add(ep); + var ep = ServiceEndpoint.Create(endPoint); + ep.Features.Set(this); + endpoints.Endpoints.Add(ep); } return default; diff --git a/src/Microsoft.Extensions.ServiceDiscovery/PassThrough/PassThroughServiceEndPointResolverProvider.cs b/src/Microsoft.Extensions.ServiceDiscovery/PassThrough/PassThroughServiceEndpointProviderFactory.cs similarity index 70% rename from src/Microsoft.Extensions.ServiceDiscovery/PassThrough/PassThroughServiceEndPointResolverProvider.cs rename to src/Microsoft.Extensions.ServiceDiscovery/PassThrough/PassThroughServiceEndpointProviderFactory.cs index 83455e0979..2bf8c0cb48 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery/PassThrough/PassThroughServiceEndPointResolverProvider.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery/PassThrough/PassThroughServiceEndpointProviderFactory.cs @@ -8,29 +8,29 @@ namespace Microsoft.Extensions.ServiceDiscovery.PassThrough; /// -/// Service endpoint resolver provider which passes through the provided value. +/// Service endpoint provider factory which creates pass-through providers. /// -internal sealed class PassThroughServiceEndPointResolverProvider(ILogger logger) : IServiceEndPointProviderFactory +internal sealed class PassThroughServiceEndpointProviderFactory(ILogger logger) : IServiceEndpointProviderFactory { /// - public bool TryCreateProvider(ServiceEndPointQuery query, [NotNullWhen(true)] out IServiceEndPointProvider? resolver) + public bool TryCreateProvider(ServiceEndpointQuery query, [NotNullWhen(true)] out IServiceEndpointProvider? provider) { - var serviceName = query.OriginalString; + var serviceName = query.ToString()!; if (!TryCreateEndPoint(serviceName, out var endPoint)) { // Propagate the value through regardless, leaving it to the caller to interpret it. endPoint = new DnsEndPoint(serviceName, 0); } - resolver = new PassThroughServiceEndPointResolver(logger, serviceName, endPoint); + provider = new PassThroughServiceEndpointProvider(logger, serviceName, endPoint); return true; } - private static bool TryCreateEndPoint(string serviceName, [NotNullWhen(true)] out EndPoint? serviceEndPoint) + private static bool TryCreateEndPoint(string serviceName, [NotNullWhen(true)] out EndPoint? endPoint) { if ((serviceName.Contains("://", StringComparison.Ordinal) || !Uri.TryCreate($"fakescheme://{serviceName}", default, out var uri)) && !Uri.TryCreate(serviceName, default, out uri)) { - serviceEndPoint = null; + endPoint = null; return false; } @@ -50,15 +50,15 @@ private static bool TryCreateEndPoint(string serviceName, [NotNullWhen(true)] ou var port = uri.Port > 0 ? uri.Port : 0; if (IPAddress.TryParse(host, out var ip)) { - serviceEndPoint = new IPEndPoint(ip, port); + endPoint = new IPEndPoint(ip, port); } else if (!string.IsNullOrEmpty(host)) { - serviceEndPoint = new DnsEndPoint(host, port); + endPoint = new DnsEndPoint(host, port); } else { - serviceEndPoint = null; + endPoint = null; return false; } diff --git a/src/Microsoft.Extensions.ServiceDiscovery/README.md b/src/Microsoft.Extensions.ServiceDiscovery/README.md index 8bece9644f..04119540e1 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery/README.md +++ b/src/Microsoft.Extensions.ServiceDiscovery/README.md @@ -6,29 +6,27 @@ In typical systems, service configuration changes over time. Service discovery a ## How it works -Service discovery uses configured _resolvers_ to resolve service endpoints. When service endpoints are resolved, each registered resolver is called in the order of registration to contribute to a collection of service endpoints (an instance of `ServiceEndPointCollection`). +Service discovery uses configured _providers_ to resolve service endpoints. When service endpoints are resolved, each registered provider is called in the order of registration to contribute to a collection of service endpoints (an instance of `ServiceEndpointSource`). -Resolvers implement the `IServiceEndPointResolver` interface. They are created by an instance of `IServiceEndPointResolverProvider`, which are registered with the [.NET dependency injection](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection) system. +Providers implement the `IServiceEndpointProvider` interface. They are created by an instance of `IServiceEndpointProviderProvider`, which are registered with the [.NET dependency injection](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection) system. -Developers typically add service discovery to their [`HttpClient`](https://learn.microsoft.com/en-us/dotnet/fundamentals/networking/http/httpclient) using the [`IHttpClientFactory`](https://learn.microsoft.com/en-us/dotnet/core/extensions/httpclient-factory) with the `UseServiceDiscovery` extension method. +Developers typically add service discovery to their [`HttpClient`](https://learn.microsoft.com/en-us/dotnet/fundamentals/networking/http/httpclient) using the [`IHttpClientFactory`](https://learn.microsoft.com/en-us/dotnet/core/extensions/httpclient-factory) with the `AddServiceDiscovery` extension method. -Services can be resolved directly by calling `ServiceEndPointResolverRegistry`'s `GetEndPointsAsync` method, which returns a collection of resolved endpoints. +Services can be resolved directly by calling `ServiceEndpointResolver`'s `GetEndpointsAsync` method, which returns a collection of resolved endpoints. ### Change notifications -Service configuration can change over time. Service discovery accounts for by monitoring endpoint configuration using push-based notifications where supported, falling back to polling in other cases. When endpoints are refreshed, callers are notified so that they can observe the refreshed results. To subscribe to notifications, callers use the `ChangeToken` property of `ServiceEndPointCollection`. For more information on change tokens, see [Detect changes with change tokens in ASP.NET Core](https://learn.microsoft.com/aspnet/core/fundamentals/change-tokens?view=aspnetcore-7.0). +Service configuration can change over time. Service discovery accounts for by monitoring endpoint configuration using push-based notifications where supported, falling back to polling in other cases. When endpoints are refreshed, callers are notified so that they can observe the refreshed results. To subscribe to notifications, callers use the `ChangeToken` property of `ServiceEndpointCollection`. For more information on change tokens, see [Detect changes with change tokens in ASP.NET Core](https://learn.microsoft.com/aspnet/core/fundamentals/change-tokens?view=aspnetcore-7.0). ### Extensibility using features -Service endpoints (`ServiceEndPoint` instances) and collections of service endpoints (`ServiceEndPointCollection` instances) expose an extensible [`IFeatureCollection`](https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.http.features.ifeaturecollection) via their `Features` property. Features are exposed as interfaces accessible on the feature collection. These interfaces can be added, modified, wrapped, replaced or even removed at resolution time by resolvers. Features which may be available on a `ServiceEndPoint` include: +Service endpoints (`ServiceEndpoint` instances) and collections of service endpoints (`ServiceEndpointCollection` instances) expose an extensible [`IFeatureCollection`](https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.http.features.ifeaturecollection) via their `Features` property. Features are exposed as interfaces accessible on the feature collection. These interfaces can be added, modified, wrapped, replaced or even removed at resolution time by providers. Features which may be available on a `ServiceEndpoint` include: * `IHostNameFeature`: exposes the host name of the resolved endpoint, intended for use with [Server Name Identification (SNI)](https://en.wikipedia.org/wiki/Server_Name_Indication) and [Transport Layer Security (TLS)](https://en.wikipedia.org/wiki/Transport_Layer_Security). -* `IEndPointHealthFeature`: used for reporting response times and errors from endpoints. -* `IEndPointLoadFeature`: used to query estimated endpoint load. ### Resolution order -The resolvers included in the `Microsoft.Extensions.ServiceDiscovery` series of packages skip resolution if there are existing endpoints in the collection when they are called. For example, consider a case where the following providers are registered: _Configuration_, _DNS SRV_, _Pass-through_. When resolution occurs, the providers will be called in-order. If the _Configuration_ providers discovers no endpoints, the _DNS SRV_ provider will perform resolution and may add one or more endpoints. If the _DNS SRV_ provider adds an endpoint to the collection, the _Pass-through_ provider will skip its resolution and will return immediately instead. +The providers included in the `Microsoft.Extensions.ServiceDiscovery` series of packages skip resolution if there are existing endpoints in the collection when they are called. For example, consider a case where the following providers are registered: _Configuration_, _DNS SRV_, _Pass-through_. When resolution occurs, the providers will be called in-order. If the _Configuration_ providers discovers no endpoints, the _DNS SRV_ provider will perform resolution and may add one or more endpoints. If the _DNS SRV_ provider adds an endpoint to the collection, the _Pass-through_ provider will skip its resolution and will return immediately instead. ## Getting Started @@ -42,19 +40,19 @@ dotnet add package Microsoft.Extensions.ServiceDiscovery ### Usage example -In the _Program.cs_ file of your project, call the `AddServiceDiscovery` extension method to add service discovery to the host, configuring default service endpoint resolvers. +In the _Program.cs_ file of your project, call the `AddServiceDiscovery` extension method to add service discovery to the host, configuring default service endpoint providers. ```csharp builder.Services.AddServiceDiscovery(); ``` -Add service discovery to an individual `IHttpClientBuilder` by calling the `UseServiceDiscovery` extension method: +Add service discovery to an individual `IHttpClientBuilder` by calling the `AddServiceDiscovery` extension method: ```csharp builder.Services.AddHttpClient(c => { c.BaseAddress = new("http://catalog")); -}).UseServiceDiscovery(); +}).AddServiceDiscovery(); ``` Alternatively, you can add service discovery to all `HttpClient` instances by default: @@ -63,14 +61,14 @@ Alternatively, you can add service discovery to all `HttpClient` instances by de builder.Services.ConfigureHttpClientDefaults(http => { // Turn on service discovery by default - http.UseServiceDiscovery(); + http.AddServiceDiscovery(); }); ``` ### Resolving service endpoints from configuration -The `AddServiceDiscovery` extension method adds a configuration-based endpoint resolver by default. -This resolver reads endpoints from the [.NET Configuration system](https://learn.microsoft.com/dotnet/core/extensions/configuration). +The `AddServiceDiscovery` extension method adds a configuration-based endpoint provider by default. +This provider reads endpoints from the [.NET Configuration system](https://learn.microsoft.com/dotnet/core/extensions/configuration). The library supports configuration through `appsettings.json`, environment variables, or any other `IConfiguration` source. Here is an example demonstrating how to configure a endpoints for the service named _catalog_ via `appsettings.json`: @@ -89,30 +87,30 @@ Here is an example demonstrating how to configure a endpoints for the service na The above example adds two endpoints for the service named _catalog_: `localhost:8080`, and `"10.46.24.90:80"`. Each time the _catalog_ is resolved, one of these endpoints will be selected. -If service discovery was added to the host using the `AddServiceDiscoveryCore` extension method on `IServiceCollection`, the configuration-based endpoint resolver can be added by calling the `AddConfigurationServiceEndPointResolver` extension method on `IServiceCollection`. +If service discovery was added to the host using the `AddServiceDiscoveryCore` extension method on `IServiceCollection`, the configuration-based endpoint provider can be added by calling the `AddConfigurationServiceEndpointProvider` extension method on `IServiceCollection`. ### Configuration -The configuration resolver is configured using the `ConfigurationServiceEndPointResolverOptions` class, which offers these configuration options: +The configuration provider is configured using the `ConfigurationServiceEndpointProviderOptions` class, which offers these configuration options: * **`SectionName`**: The name of the configuration section that contains service endpoints. It defaults to `"Services"`. -* **`ApplyHostNameMetadata`**: A delegate used to determine if host name metadata should be applied to resolved endpoints. It defaults to a function that returns `false`. +* **`ShouldApplyHostNameMetadata`**: A delegate used to determine if host name metadata should be applied to resolved endpoints. It defaults to a function that returns `false`. To configure these options, you can use the `Configure` extension method on the `IServiceCollection` within your application's startup class or main program file: ```csharp var builder = WebApplication.CreateBuilder(args); -builder.Services.Configure(options => +builder.Services.Configure(options => { options.SectionName = "MyServiceEndpoints"; // Configure the logic for applying host name metadata - options.ApplyHostNameMetadata = endpoint => + options.ShouldApplyHostNameMetadata = endpoint => { // Your custom logic here. For example: - return endpoint.EndPoint is DnsEndPoint dnsEp && dnsEp.Host.StartsWith("internal"); + return endpoint.Endpoint is DnsEndPoint dnsEp && dnsEp.Host.StartsWith("internal"); }; }); ``` @@ -121,38 +119,38 @@ This example demonstrates setting a custom section name for your service endpoin ## Resolving service endpoints using platform-provided service discovery -Some platforms, such as Azure Container Apps and Kubernetes (if configured), provide functionality for service discovery without the need for a service discovery client library. When an application is deployed to one of these environments, it may be preferable to use the platform's existing functionality instead. The pass-through resolver exists to support this scenario while still allowing other resolvers (such as configuration) to be used in other environments, such as on the developer's machine, without requiring a code change or conditional guards. +Some platforms, such as Azure Container Apps and Kubernetes (if configured), provide functionality for service discovery without the need for a service discovery client library. When an application is deployed to one of these environments, it may be preferable to use the platform's existing functionality instead. The pass-through provider exists to support this scenario while still allowing other provider (such as configuration) to be used in other environments, such as on the developer's machine, without requiring a code change or conditional guards. -The pass-through resolver performs no external resolution and instead resolves endpoints by returning the input service name represented as a `DnsEndPoint`. +The pass-through provider performs no external resolution and instead resolves endpoints by returning the input service name represented as a `DnsEndPoint`. The pass-through provider is configured by-default when adding service discovery via the `AddServiceDiscovery` extension method. -If service discovery was added to the host using the `AddServiceDiscoveryCore` extension method on `IServiceCollection`, the pass-through provider can be added by calling the `AddPassThroughServiceEndPointResolver` extension method on `IServiceCollection`. +If service discovery was added to the host using the `AddServiceDiscoveryCore` extension method on `IServiceCollection`, the pass-through provider can be added by calling the `AddPassThroughServiceEndpointProvider` extension method on `IServiceCollection`. In the case of Azure Container Apps, the service name should match the app name. For example, if you have a service named "basket", then you should have a corresponding Azure Container App named "basket". ## Load-balancing with endpoint selectors -Each time an endpoint is resolved by the `HttpClient` pipeline, a single endpoint will be selected from the set of all known endpoints for the requested service. If multiple endpoints are available, it may be desirable to balance traffic across all such endpoints. To accomplish this, a customizable _endpoint selector_ can be used. By default, endpoints are selected in round-robin order. To use a different endpoint selector, provide an `IServiceEndPointSelector` instance to the `UseServiceDiscovery` method call. For example, to select a random endpoint from the set of resolved endpoints, specify `RandomServiceEndPointSelector.Instance` as the endpoint selector: +Each time an endpoint is resolved by the `HttpClient` pipeline, a single endpoint will be selected from the set of all known endpoints for the requested service. If multiple endpoints are available, it may be desirable to balance traffic across all such endpoints. To accomplish this, a customizable _endpoint selector_ can be used. By default, endpoints are selected in round-robin order. To use a different endpoint selector, provide an `IServiceEndpointSelector` instance to the `AddServiceDiscovery` method call. For example, to select a random endpoint from the set of resolved endpoints, specify `RandomServiceEndpointSelector.Instance` as the endpoint selector: ```csharp builder.Services.AddHttpClient( static client => client.BaseAddress = new("http://catalog")); - .UseServiceDiscovery(RandomServiceEndPointSelector.Instance); + .AddServiceDiscovery(RandomServiceEndpointSelector.Instance); ``` The _Microsoft.Extensions.ServiceDiscovery_ package includes the following endpoint selector providers: -* Pick-first, which always selects the first endpoint: `PickFirstServiceEndPointSelectorProvider.Instance` -* Round-robin, which cycles through endpoints: `RoundRobinServiceEndPointSelectorProvider.Instance` -* Random, which selects endpoints randomly: `RandomServiceEndPointSelectorProvider.Instance` -* Power-of-two-choices, which attempts to pick the least heavily loaded endpoint based on the _Power of Two Choices_ algorithm for distributed load balancing, degrading to randomly selecting an endpoint when either of the provided endpoints do not have the `IEndPointLoadFeature` feature: `PowerOfTwoChoicesServiceEndPointSelectorProvider.Instance` +* Pick-first, which always selects the first endpoint: `PickFirstServiceEndpointSelectorProvider.Instance` +* Round-robin, which cycles through endpoints: `RoundRobinServiceEndpointSelectorProvider.Instance` +* Random, which selects endpoints randomly: `RandomServiceEndpointSelectorProvider.Instance` +* Power-of-two-choices, which attempts to pick the least heavily loaded endpoint based on the _Power of Two Choices_ algorithm for distributed load balancing, degrading to randomly selecting an endpoint when either of the provided endpoints do not have the `IEndpointLoadFeature` feature: `PowerOfTwoChoicesServiceEndpointSelectorProvider.Instance` -Endpoint selectors are created via an `IServiceEndPointSelectorProvider` instance, such as those listed above. The provider's `CreateSelector()` method is called to create a selector, which is an instance of `IServiceEndPointSelector`. The `IServiceEndPointSelector` instance is given the set of known endpoints when they are resolved, using the `SetEndPoints(ServiceEndPointCollection collection)` method. To choose an endpoint from the collection, the `GetEndPoint(object? context)` method is called, returning a single `ServiceEndPoint`. The `context` value passed to `GetEndPoint` is used to provide extra context which may be useful to the selector. For example, in the `HttpClient` case, the `HttpRequestMessage` is passed. None of the provided implementations of `IServiceEndPointSelector` inspect the context, and it can be ignored unless you are using a selector which does make use of it. +Endpoint selectors are created via an `IServiceEndpointSelectorProvider` instance, such as those listed above. The provider's `CreateSelector()` method is called to create a selector, which is an instance of `IServiceEndpointSelector`. The `IServiceEndpointSelector` instance is given the set of known endpoints when they are resolved, using the `SetEndpoints(ServiceEndpointCollection collection)` method. To choose an endpoint from the collection, the `GetEndpoint(object? context)` method is called, returning a single `ServiceEndpoint`. The `context` value passed to `GetEndpoint` is used to provide extra context which may be useful to the selector. For example, in the `HttpClient` case, the `HttpRequestMessage` is passed. None of the provided implementations of `IServiceEndpointSelector` inspect the context, and it can be ignored unless you are using a selector which does make use of it. ## Service discovery in .NET Aspire -.NET Aspire includes functionality for configuring the service discovery at development and testing time. This functionality works by providing configuration in the format expected by the _configuration-based endpoint resolver_ described above from the .NET Aspire AppHost project to the individual service projects added to the application model. +.NET Aspire includes functionality for configuring the service discovery at development and testing time. This functionality works by providing configuration in the format expected by the _configuration-based endpoint provider_ described above from the .NET Aspire AppHost project to the individual service projects added to the application model. Configuration for service discovery is only added for services which are referenced by a given project. For example, consider the following AppHost program: @@ -184,7 +182,7 @@ In the above example, two `HttpClient`s are added: one for the core basket servi ### Named endpoints using configuration -With the configuration-based endpoint resolver, named endpoints can be specified in configuration by prefixing the endpoint value with `_endpointName.`, where `endpointName` is the endpoint name. For example, consider this _appsettings.json_ configuration which defined a default endpoint (with no name) and an endpoint named "dashboard": +With the configuration-based endpoint provider, named endpoints can be specified in configuration by prefixing the endpoint value with `_endpointName.`, where `endpointName` is the endpoint name. For example, consider this _appsettings.json_ configuration which defined a default endpoint (with no name) and an endpoint named "dashboard": ```json { @@ -199,7 +197,7 @@ With the configuration-based endpoint resolver, named endpoints can be specified ### Named endpoints in .NET Aspire -.NET Aspire uses the configuration-based resolver at development and testing time, providing convenient APIs for configuring named endpoints which are then translated into configuration for the target services. For example: +.NET Aspire uses the configuration-based provider at development and testing time, providing convenient APIs for configuring named endpoints which are then translated into configuration for the target services. For example: ```csharp var builder = DistributedApplication.CreateBuilder(args); @@ -208,13 +206,13 @@ var basket = builder.AddProject("basket") .WithEndpoint(hostPort: 9999, scheme: "http", name: "admin"); var adminDashboard = builder.AddProject("admin-dashboard") - .WithReference(basket.GetEndPoint("admin")); + .WithReference(basket.GetEndpoint("admin")); var frontend = builder.AddProject("frontend") .WithReference(basket); ``` -In the above example, the "basket" service exposes an "admin" endpoint in addition to the default "http" endpoint which it exposes. This endpoint is consumed by the "admin-dashboard" project, while the "frontend" project consumes all endpoints from "basket". Alternatively, the "frontend" project could be made to consume only the default "http" endpoint from "basket" by using the `GetEndPoint(string name)` method, as in the following example: +In the above example, the "basket" service exposes an "admin" endpoint in addition to the default "http" endpoint which it exposes. This endpoint is consumed by the "admin-dashboard" project, while the "frontend" project consumes all endpoints from "basket". Alternatively, the "frontend" project could be made to consume only the default "http" endpoint from "basket" by using the `GetEndpoint(string name)` method, as in the following example: ```csharp @@ -226,7 +224,7 @@ var frontend = builder.AddProject("frontend") ### Named endpoints in Kubernetes using DNS SRV -When deploying to Kubernetes, the DNS SRV service endpoint resolver can be used to resolve named endpoints. For example, the following resource definition will result in a DNS SRV record being created for an endpoint named "default" and an endpoint named "dashboard", both on the service named "basket". +When deploying to Kubernetes, the DNS SRV service endpoint provider can be used to resolve named endpoints. For example, the following resource definition will result in a DNS SRV record being created for an endpoint named "default" and an endpoint named "dashboard", both on the service named "basket". ```yml apiVersion: v1 @@ -244,11 +242,11 @@ spec: port: 8888 ``` -To configure a service to resolve the "dashboard" endpoint on the "basket" service, add the DNS SRV service endpoint resolver to the host builder as follows: +To configure a service to resolve the "dashboard" endpoint on the "basket" service, add the DNS SRV service endpoint provider to the host builder as follows: ```csharp builder.Services.AddServiceDiscoveryCore(); -builder.Services.AddDnsSrvServiceEndPointResolver(); +builder.Services.AddDnsSrvServiceEndpointProvider(); ``` The special port name "default" is used to specify the default endpoint, resolved using the URI `http://basket`. diff --git a/src/Microsoft.Extensions.ServiceDiscovery/ServiceDiscoveryHttpClientBuilderExtensions.cs b/src/Microsoft.Extensions.ServiceDiscovery/ServiceDiscoveryHttpClientBuilderExtensions.cs index b4c34ccb7c..8a137aad4f 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery/ServiceDiscoveryHttpClientBuilderExtensions.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery/ServiceDiscoveryHttpClientBuilderExtensions.cs @@ -26,8 +26,8 @@ public static IHttpClientBuilder AddServiceDiscovery(this IHttpClientBuilder htt httpClientBuilder.AddHttpMessageHandler(services => { var timeProvider = services.GetService() ?? TimeProvider.System; - var resolverProvider = services.GetRequiredService(); - var registry = new HttpServiceEndPointResolver(resolverProvider, services, timeProvider); + var watcherFactory = services.GetRequiredService(); + var registry = new HttpServiceEndpointResolver(watcherFactory, services, timeProvider); var options = services.GetRequiredService>(); return new ResolvingHttpDelegatingHandler(registry, options); }); diff --git a/src/Microsoft.Extensions.ServiceDiscovery/ServiceDiscoveryOptions.cs b/src/Microsoft.Extensions.ServiceDiscovery/ServiceDiscoveryOptions.cs index 89c5a2d2eb..02ce1af162 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery/ServiceDiscoveryOptions.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery/ServiceDiscoveryOptions.cs @@ -6,21 +6,19 @@ namespace Microsoft.Extensions.ServiceDiscovery; /// -/// Options for service endpoint resolvers. +/// Options for service endpoint resolution. /// public sealed class ServiceDiscoveryOptions { /// - /// The value indicating that all endpoint schemes are allowed. + /// Gets or sets a value indicating whether all URI schemes for URIs resolved by the service discovery system are allowed. + /// If this value is , all URI schemes are allowed. + /// If this value is , only the schemes specified in are allowed. /// -#pragma warning disable IDE0300 // Simplify collection initialization -#pragma warning disable CA1825 // Avoid zero-length array allocations - public static readonly string[] AllowAllSchemes = new string[0]; -#pragma warning restore CA1825 // Avoid zero-length array allocations -#pragma warning restore IDE0300 // Simplify collection initialization + public bool AllowAllSchemes { get; set; } = true; /// - /// Gets or sets the period between polling attempts for resolvers which do not support refresh notifications via . + /// Gets or sets the period between polling attempts for providers which do not support refresh notifications via . /// public TimeSpan RefreshPeriod { get; set; } = TimeSpan.FromSeconds(60); @@ -28,14 +26,13 @@ public sealed class ServiceDiscoveryOptions /// Gets or sets the collection of allowed URI schemes for URIs resolved by the service discovery system when multiple schemes are specified, for example "https+http://_endpoint.service". /// /// - /// When set to , all schemes are allowed. - /// Schemes are not case-sensitive. + /// When is , this property is ignored. /// - public string[] AllowedSchemes { get; set; } = AllowAllSchemes; + public IList AllowedSchemes { get; set; } = new List(); - internal static string[] ApplyAllowedSchemes(IReadOnlyList schemes, IReadOnlyList allowedSchemes) + internal static string[] ApplyAllowedSchemes(IReadOnlyList schemes, IList allowedSchemes, bool allowAllSchemes) { - if (allowedSchemes.Equals(AllowAllSchemes)) + if (allowAllSchemes) { if (schemes is string[] array) { diff --git a/src/Microsoft.Extensions.ServiceDiscovery/ServiceDiscoveryServiceCollectionExtensions.cs b/src/Microsoft.Extensions.ServiceDiscovery/ServiceDiscoveryServiceCollectionExtensions.cs index 6403b21463..a5d789b7e4 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery/ServiceDiscoveryServiceCollectionExtensions.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery/ServiceDiscoveryServiceCollectionExtensions.cs @@ -26,8 +26,8 @@ public static class ServiceDiscoveryServiceCollectionExtensions public static IServiceCollection AddServiceDiscovery(this IServiceCollection services) { return services.AddServiceDiscoveryCore() - .AddConfigurationServiceEndPointResolver() - .AddPassThroughServiceEndPointResolver(); + .AddConfigurationServiceEndpointProvider() + .AddPassThroughServiceEndpointProvider(); } /// @@ -36,11 +36,11 @@ public static IServiceCollection AddServiceDiscovery(this IServiceCollection ser /// The service collection. /// The delegate used to configure service discovery options. /// The service collection. - public static IServiceCollection AddServiceDiscovery(this IServiceCollection services, Action? configureOptions) + public static IServiceCollection AddServiceDiscovery(this IServiceCollection services, Action configureOptions) { return services.AddServiceDiscoveryCore(configureOptions: configureOptions) - .AddConfigurationServiceEndPointResolver() - .AddPassThroughServiceEndPointResolver(); + .AddConfigurationServiceEndpointProvider() + .AddPassThroughServiceEndpointProvider(); } /// @@ -48,7 +48,7 @@ public static IServiceCollection AddServiceDiscovery(this IServiceCollection ser /// /// The service collection. /// The service collection. - public static IServiceCollection AddServiceDiscoveryCore(this IServiceCollection services) => services.AddServiceDiscoveryCore(configureOptions: null); + public static IServiceCollection AddServiceDiscoveryCore(this IServiceCollection services) => services.AddServiceDiscoveryCore(configureOptions: _ => { }); /// /// Adds the core service discovery services. @@ -56,16 +56,16 @@ public static IServiceCollection AddServiceDiscovery(this IServiceCollection ser /// The service collection. /// The delegate used to configure service discovery options. /// The service collection. - public static IServiceCollection AddServiceDiscoveryCore(this IServiceCollection services, Action? configureOptions) + public static IServiceCollection AddServiceDiscoveryCore(this IServiceCollection services, Action configureOptions) { services.AddOptions(); services.AddLogging(); services.TryAddTransient, ServiceDiscoveryOptionsValidator>(); services.TryAddSingleton(_ => TimeProvider.System); - services.TryAddTransient(); - services.TryAddSingleton(); + services.TryAddTransient(); + services.TryAddSingleton(); services.TryAddSingleton(); - services.TryAddSingleton(sp => new ServiceEndPointResolver(sp.GetRequiredService(), sp.GetRequiredService())); + services.TryAddSingleton(sp => new ServiceEndpointResolver(sp.GetRequiredService(), sp.GetRequiredService())); if (configureOptions is not null) { services.Configure(configureOptions); @@ -75,26 +75,26 @@ public static IServiceCollection AddServiceDiscoveryCore(this IServiceCollection } /// - /// Configures a service discovery endpoint resolver which uses to resolve endpoints. + /// Configures a service discovery endpoint provider which uses to resolve endpoints. /// /// The service collection. /// The service collection. - public static IServiceCollection AddConfigurationServiceEndPointResolver(this IServiceCollection services) + public static IServiceCollection AddConfigurationServiceEndpointProvider(this IServiceCollection services) { - return services.AddConfigurationServiceEndPointResolver(configureOptions: null); + return services.AddConfigurationServiceEndpointProvider(configureOptions: _ => { }); } /// - /// Configures a service discovery endpoint resolver which uses to resolve endpoints. + /// Configures a service discovery endpoint provider which uses to resolve endpoints. /// /// The delegate used to configure the provider. /// The service collection. /// The service collection. - public static IServiceCollection AddConfigurationServiceEndPointResolver(this IServiceCollection services, Action? configureOptions) + public static IServiceCollection AddConfigurationServiceEndpointProvider(this IServiceCollection services, Action configureOptions) { services.AddServiceDiscoveryCore(); - services.AddSingleton(); - services.AddTransient, ConfigurationServiceEndPointResolverOptionsValidator>(); + services.AddSingleton(); + services.AddTransient, ConfigurationServiceEndpointProviderOptionsValidator>(); if (configureOptions is not null) { services.Configure(configureOptions); @@ -104,14 +104,14 @@ public static IServiceCollection AddConfigurationServiceEndPointResolver(this IS } /// - /// Configures a service discovery endpoint resolver which passes through the input without performing resolution. + /// Configures a service discovery endpoint provider which passes through the input without performing resolution. /// /// The service collection. /// The service collection. - public static IServiceCollection AddPassThroughServiceEndPointResolver(this IServiceCollection services) + public static IServiceCollection AddPassThroughServiceEndpointProvider(this IServiceCollection services) { services.AddServiceDiscoveryCore(); - services.AddSingleton(); + services.AddSingleton(); return services; } } diff --git a/src/Microsoft.Extensions.ServiceDiscovery/ServiceEndPointWatcherFactory.cs b/src/Microsoft.Extensions.ServiceDiscovery/ServiceEndPointWatcherFactory.cs deleted file mode 100644 index 90f62ab059..0000000000 --- a/src/Microsoft.Extensions.ServiceDiscovery/ServiceEndPointWatcherFactory.cs +++ /dev/null @@ -1,61 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Microsoft.Extensions.ServiceDiscovery.PassThrough; - -namespace Microsoft.Extensions.ServiceDiscovery; - -/// -/// Creates service endpoint watchers. -/// -internal sealed partial class ServiceEndPointWatcherFactory( - IEnumerable resolvers, - ILogger resolverLogger, - IOptions options, - TimeProvider timeProvider) -{ - private readonly IServiceEndPointProviderFactory[] _resolverProviders = resolvers - .Where(r => r is not PassThroughServiceEndPointResolverProvider) - .Concat(resolvers.Where(static r => r is PassThroughServiceEndPointResolverProvider)).ToArray(); - private readonly ILogger _logger = resolverLogger; - private readonly TimeProvider _timeProvider = timeProvider; - private readonly IOptions _options = options; - - /// - /// Creates a service endpoint resolver for the provided service name. - /// - public ServiceEndPointWatcher CreateWatcher(string serviceName) - { - ArgumentNullException.ThrowIfNull(serviceName); - - if (!ServiceEndPointQuery.TryParse(serviceName, out var query)) - { - throw new ArgumentException("The provided input was not in a valid format. It must be a valid URI.", nameof(serviceName)); - } - - List? resolvers = null; - foreach (var factory in _resolverProviders) - { - if (factory.TryCreateProvider(query, out var resolver)) - { - resolvers ??= []; - resolvers.Add(resolver); - } - } - - if (resolvers is not { Count: > 0 }) - { - throw new InvalidOperationException($"No resolver which supports the provided service name, '{serviceName}', has been configured."); - } - - Log.CreatingResolver(_logger, serviceName, resolvers); - return new ServiceEndPointWatcher( - resolvers: [.. resolvers], - logger: _logger, - serviceName: serviceName, - timeProvider: _timeProvider, - options: _options); - } -} diff --git a/src/Microsoft.Extensions.ServiceDiscovery/ServiceEndPointBuilder.cs b/src/Microsoft.Extensions.ServiceDiscovery/ServiceEndpointBuilder.cs similarity index 75% rename from src/Microsoft.Extensions.ServiceDiscovery/ServiceEndPointBuilder.cs rename to src/Microsoft.Extensions.ServiceDiscovery/ServiceEndpointBuilder.cs index 1a14cb961b..947f24b2f8 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery/ServiceEndPointBuilder.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery/ServiceEndpointBuilder.cs @@ -9,9 +9,9 @@ namespace Microsoft.Extensions.ServiceDiscovery; /// /// A mutable collection of service endpoints. /// -internal sealed class ServiceEndPointBuilder : IServiceEndPointBuilder +internal sealed class ServiceEndpointBuilder : IServiceEndpointBuilder { - private readonly List _endPoints = new(); + private readonly List _endpoints = new(); private readonly List _changeTokens = new(); private readonly FeatureCollection _features = new FeatureCollection(); @@ -32,15 +32,15 @@ public void AddChangeToken(IChangeToken changeToken) /// /// Gets the endpoints. /// - public IList EndPoints => _endPoints; + public IList Endpoints => _endpoints; /// - /// Creates a from the provided instance. + /// Creates a from the provided instance. /// /// The service endpoint source. - public ServiceEndPointSource Build() + public ServiceEndpointSource Build() { - return new ServiceEndPointSource(_endPoints, new CompositeChangeToken(_changeTokens), _features); + return new ServiceEndpointSource(_endpoints, new CompositeChangeToken(_changeTokens), _features); } } diff --git a/src/Microsoft.Extensions.ServiceDiscovery/ServiceEndPointResolver.cs b/src/Microsoft.Extensions.ServiceDiscovery/ServiceEndpointResolver.cs similarity index 84% rename from src/Microsoft.Extensions.ServiceDiscovery/ServiceEndPointResolver.cs rename to src/Microsoft.Extensions.ServiceDiscovery/ServiceEndpointResolver.cs index 029d260124..92df120940 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery/ServiceEndPointResolver.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery/ServiceEndpointResolver.cs @@ -9,13 +9,13 @@ namespace Microsoft.Extensions.ServiceDiscovery; /// /// Resolves service names to collections of endpoints. /// -public sealed class ServiceEndPointResolver : IAsyncDisposable +public sealed class ServiceEndpointResolver : IAsyncDisposable { - private static readonly TimerCallback s_cleanupCallback = s => ((ServiceEndPointResolver)s!).CleanupResolvers(); + private static readonly TimerCallback s_cleanupCallback = s => ((ServiceEndpointResolver)s!).CleanupResolvers(); private static readonly TimeSpan s_cleanupPeriod = TimeSpan.FromSeconds(10); private readonly object _lock = new(); - private readonly ServiceEndPointWatcherFactory _resolverProvider; + private readonly ServiceEndpointWatcherFactory _watcherFactory; private readonly TimeProvider _timeProvider; private readonly ConcurrentDictionary _resolvers = new(); private ITimer? _cleanupTimer; @@ -23,13 +23,13 @@ public sealed class ServiceEndPointResolver : IAsyncDisposable private bool _disposed; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The resolver factory. + /// The watcher factory. /// The time provider. - internal ServiceEndPointResolver(ServiceEndPointWatcherFactory resolverProvider, TimeProvider timeProvider) + internal ServiceEndpointResolver(ServiceEndpointWatcherFactory watcherFactory, TimeProvider timeProvider) { - _resolverProvider = resolverProvider; + _watcherFactory = watcherFactory; _timeProvider = timeProvider; } @@ -39,7 +39,7 @@ internal ServiceEndPointResolver(ServiceEndPointWatcherFactory resolverProvider, /// The service name. /// A . /// The resolved service endpoints. - public async ValueTask GetEndPointsAsync(string serviceName, CancellationToken cancellationToken) + public async ValueTask GetEndpointsAsync(string serviceName, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(serviceName); ObjectDisposedException.ThrowIf(_disposed, this); @@ -54,7 +54,7 @@ public async ValueTask GetEndPointsAsync(string serviceNa static (name, self) => self.CreateResolver(name), this); - var (valid, result) = await resolver.GetEndPointsAsync(cancellationToken).ConfigureAwait(false); + var (valid, result) = await resolver.GetEndpointsAsync(cancellationToken).ConfigureAwait(false); if (valid) { if (result is null) @@ -156,21 +156,21 @@ private async Task CleanupResolversAsyncCore() private ResolverEntry CreateResolver(string serviceName) { - var resolver = _resolverProvider.CreateWatcher(serviceName); + var resolver = _watcherFactory.CreateWatcher(serviceName); resolver.Start(); return new ResolverEntry(resolver); } - private sealed class ResolverEntry(ServiceEndPointWatcher resolver) : IAsyncDisposable + private sealed class ResolverEntry(ServiceEndpointWatcher watcher) : IAsyncDisposable { - private readonly ServiceEndPointWatcher _resolver = resolver; + private readonly ServiceEndpointWatcher _watcher = watcher; private const ulong CountMask = ~(RecentUseFlag | DisposingFlag); private const ulong RecentUseFlag = 1UL << 62; private const ulong DisposingFlag = 1UL << 63; private ulong _status; private TaskCompletionSource? _onDisposed; - public string ServiceName => _resolver.ServiceName; + public string ServiceName => _watcher.ServiceName; public bool CanExpire() { @@ -181,17 +181,17 @@ public bool CanExpire() return (status & (CountMask | RecentUseFlag)) == 0; } - public async ValueTask<(bool Valid, ServiceEndPointSource? EndPoints)> GetEndPointsAsync(CancellationToken cancellationToken) + public async ValueTask<(bool Valid, ServiceEndpointSource? Endpoints)> GetEndpointsAsync(CancellationToken cancellationToken) { try { var status = Interlocked.Increment(ref _status); if ((status & DisposingFlag) == 0) { - // If the resolver is valid, resolve. + // If the watcher is valid, resolve. // We ensure that it will not be disposed while we are resolving. - var endPoints = await _resolver.GetEndPointsAsync(cancellationToken).ConfigureAwait(false); - return (true, endPoints); + var endpoints = await _watcher.GetEndpointsAsync(cancellationToken).ConfigureAwait(false); + return (true, endpoints); } else { @@ -237,7 +237,7 @@ private async Task DisposeAsyncCore() { try { - await _resolver.DisposeAsync().ConfigureAwait(false); + await _watcher.DisposeAsync().ConfigureAwait(false); } finally { diff --git a/src/Microsoft.Extensions.ServiceDiscovery/ServiceEndPointWatcher.Log.cs b/src/Microsoft.Extensions.ServiceDiscovery/ServiceEndpointWatcher.Log.cs similarity index 60% rename from src/Microsoft.Extensions.ServiceDiscovery/ServiceEndPointWatcher.Log.cs rename to src/Microsoft.Extensions.ServiceDiscovery/ServiceEndpointWatcher.Log.cs index 78a8f84b55..fce9f667b4 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery/ServiceEndPointWatcher.Log.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery/ServiceEndpointWatcher.Log.cs @@ -5,35 +5,35 @@ namespace Microsoft.Extensions.ServiceDiscovery; -partial class ServiceEndPointWatcher +partial class ServiceEndpointWatcher { private sealed partial class Log { - [LoggerMessage(1, LogLevel.Debug, "Resolving endpoints for service '{ServiceName}'.", EventName = "ResolvingEndPoints")] - public static partial void ResolvingEndPoints(ILogger logger, string serviceName); + [LoggerMessage(1, LogLevel.Debug, "Resolving endpoints for service '{ServiceName}'.", EventName = "ResolvingEndpoints")] + public static partial void ResolvingEndpoints(ILogger logger, string serviceName); [LoggerMessage(2, LogLevel.Debug, "Endpoint resolution is pending for service '{ServiceName}'.", EventName = "ResolutionPending")] public static partial void ResolutionPending(ILogger logger, string serviceName); - [LoggerMessage(3, LogLevel.Debug, "Resolved {Count} endpoints for service '{ServiceName}': {EndPoints}.", EventName = "ResolutionSucceeded")] - public static partial void ResolutionSucceededCore(ILogger logger, int count, string serviceName, string endPoints); + [LoggerMessage(3, LogLevel.Debug, "Resolved {Count} endpoints for service '{ServiceName}': {Endpoints}.", EventName = "ResolutionSucceeded")] + public static partial void ResolutionSucceededCore(ILogger logger, int count, string serviceName, string endpoints); - public static void ResolutionSucceeded(ILogger logger, string serviceName, ServiceEndPointSource endPointSource) + public static void ResolutionSucceeded(ILogger logger, string serviceName, ServiceEndpointSource endpointSource) { if (logger.IsEnabled(LogLevel.Debug)) { - ResolutionSucceededCore(logger, endPointSource.EndPoints.Count, serviceName, string.Join(", ", endPointSource.EndPoints.Select(GetEndPointString))); + ResolutionSucceededCore(logger, endpointSource.Endpoints.Count, serviceName, string.Join(", ", endpointSource.Endpoints.Select(GetEndpointString))); } - static string GetEndPointString(ServiceEndPoint ep) + static string GetEndpointString(ServiceEndpoint ep) { - if (ep.Features.Get() is { } resolver) + if (ep.Features.Get() is { } provider) { - return $"{ep.GetEndPointString()} ({resolver})"; + return $"{ep} ({provider})"; } - return ep.GetEndPointString(); - } + return ep.ToString()!; + } } [LoggerMessage(4, LogLevel.Error, "Error resolving endpoints for service '{ServiceName}'.", EventName = "ResolutionFailed")] diff --git a/src/Microsoft.Extensions.ServiceDiscovery/ServiceEndPointWatcher.cs b/src/Microsoft.Extensions.ServiceDiscovery/ServiceEndpointWatcher.cs similarity index 75% rename from src/Microsoft.Extensions.ServiceDiscovery/ServiceEndPointWatcher.cs rename to src/Microsoft.Extensions.ServiceDiscovery/ServiceEndpointWatcher.cs index 9b1069d31e..ba6df9b43c 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery/ServiceEndPointWatcher.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery/ServiceEndpointWatcher.cs @@ -13,23 +13,23 @@ namespace Microsoft.Extensions.ServiceDiscovery; /// /// Watches for updates to the collection of resolved endpoints for a specified service. /// -internal sealed partial class ServiceEndPointWatcher( - IServiceEndPointProvider[] resolvers, +internal sealed partial class ServiceEndpointWatcher( + IServiceEndpointProvider[] providers, ILogger logger, string serviceName, TimeProvider timeProvider, IOptions options) : IAsyncDisposable { - private static readonly TimerCallback s_pollingAction = static state => _ = ((ServiceEndPointWatcher)state!).RefreshAsync(force: true); + private static readonly TimerCallback s_pollingAction = static state => _ = ((ServiceEndpointWatcher)state!).RefreshAsync(force: true); private readonly object _lock = new(); private readonly ILogger _logger = logger; private readonly TimeProvider _timeProvider = timeProvider; private readonly ServiceDiscoveryOptions _options = options.Value; - private readonly IServiceEndPointProvider[] _resolvers = resolvers; + private readonly IServiceEndpointProvider[] _providers = providers; private readonly CancellationTokenSource _disposalCancellation = new(); private ITimer? _pollingTimer; - private ServiceEndPointSource? _cachedEndPoints; + private ServiceEndpointSource? _cachedEndpoints; private Task _refreshTask = Task.CompletedTask; private volatile CacheStatus _cacheState; @@ -41,14 +41,14 @@ internal sealed partial class ServiceEndPointWatcher( /// /// Gets or sets the action called when endpoints are updated. /// - public Action? OnEndPointsUpdated { get; set; } + public Action? OnEndpointsUpdated { get; set; } /// /// Starts the endpoint resolver. /// public void Start() { - ThrowIfNoResolvers(); + ThrowIfNoProviders(); _ = RefreshAsync(force: false); } @@ -57,27 +57,27 @@ public void Start() /// /// A . /// A collection of resolved endpoints for the service. - public ValueTask GetEndPointsAsync(CancellationToken cancellationToken = default) + public ValueTask GetEndpointsAsync(CancellationToken cancellationToken = default) { - ThrowIfNoResolvers(); + ThrowIfNoProviders(); // If the cache is valid, return the cached value. - if (_cachedEndPoints is { ChangeToken.HasChanged: false } cached) + if (_cachedEndpoints is { ChangeToken.HasChanged: false } cached) { - return new ValueTask(cached); + return new ValueTask(cached); } // Otherwise, ensure the cache is being refreshed // Wait for the cache refresh to complete and return the cached value. - return GetEndPointsInternal(cancellationToken); + return GetEndpointsInternal(cancellationToken); - async ValueTask GetEndPointsInternal(CancellationToken cancellationToken) + async ValueTask GetEndpointsInternal(CancellationToken cancellationToken) { - ServiceEndPointSource? result; + ServiceEndpointSource? result; do { await RefreshAsync(force: false).WaitAsync(cancellationToken).ConfigureAwait(false); - result = _cachedEndPoints; + result = _cachedEndpoints; } while (result is null); return result; } @@ -89,7 +89,7 @@ private Task RefreshAsync(bool force) lock (_lock) { // If the cache is invalid or needs invalidation, refresh the cache. - if (_refreshTask.IsCompleted && (_cacheState == CacheStatus.Invalid || _cachedEndPoints is null or { ChangeToken.HasChanged: true } || force)) + if (_refreshTask.IsCompleted && (_cacheState == CacheStatus.Invalid || _cachedEndpoints is null or { ChangeToken.HasChanged: true } || force)) { // Indicate that the cache is being updated and start a new refresh task. _cacheState = CacheStatus.Refreshing; @@ -124,27 +124,27 @@ private async Task RefreshAsyncInternal() await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding); var cancellationToken = _disposalCancellation.Token; Exception? error = null; - ServiceEndPointSource? newEndPoints = null; + ServiceEndpointSource? newEndpoints = null; CacheStatus newCacheState; try { - Log.ResolvingEndPoints(_logger, ServiceName); - var builder = new ServiceEndPointBuilder(); - foreach (var resolver in _resolvers) + Log.ResolvingEndpoints(_logger, ServiceName); + var builder = new ServiceEndpointBuilder(); + foreach (var provider in _providers) { - await resolver.PopulateAsync(builder, cancellationToken).ConfigureAwait(false); + await provider.PopulateAsync(builder, cancellationToken).ConfigureAwait(false); } - var endPoints = builder.Build(); + var endpoints = builder.Build(); newCacheState = CacheStatus.Valid; lock (_lock) { // Check if we need to poll for updates or if we can register for change notification callbacks. - if (endPoints.ChangeToken.ActiveChangeCallbacks) + if (endpoints.ChangeToken.ActiveChangeCallbacks) { // Initiate a background refresh, if necessary. - endPoints.ChangeToken.RegisterChangeCallback(static state => _ = ((ServiceEndPointWatcher)state!).RefreshAsync(force: false), this); + endpoints.ChangeToken.RegisterChangeCallback(static state => _ = ((ServiceEndpointWatcher)state!).RefreshAsync(force: false), this); if (_pollingTimer is { } timer) { _pollingTimer = null; @@ -157,7 +157,7 @@ private async Task RefreshAsyncInternal() } // The cache is valid - newEndPoints = endPoints; + newEndpoints = endpoints; newCacheState = CacheStatus.Valid; } } @@ -171,26 +171,26 @@ private async Task RefreshAsyncInternal() // If there was an error, the cache must be invalid. Debug.Assert(error is null || newCacheState is CacheStatus.Invalid); - // To ensure coherence between the value returned by calls made to GetEndPointsAsync and value passed to the callback, + // To ensure coherence between the value returned by calls made to GetEndpointsAsync and value passed to the callback, // we invalidate the cache before invoking the callback. This causes callers to wait on the refresh task - // before receiving the updated value. An alternative approach is to lock access to _cachedEndPoints, but + // before receiving the updated value. An alternative approach is to lock access to _cachedEndpoints, but // that will have more overhead in the common case. if (newCacheState is CacheStatus.Valid) { - Interlocked.Exchange(ref _cachedEndPoints, null); + Interlocked.Exchange(ref _cachedEndpoints, null); } - if (OnEndPointsUpdated is { } callback) + if (OnEndpointsUpdated is { } callback) { - callback(new(newEndPoints, error)); + callback(new(newEndpoints, error)); } lock (_lock) { if (newCacheState is CacheStatus.Valid) { - Debug.Assert(newEndPoints is not null); - _cachedEndPoints = newEndPoints; + Debug.Assert(newEndpoints is not null); + _cachedEndpoints = newEndpoints; } _cacheState = newCacheState; @@ -201,9 +201,9 @@ private async Task RefreshAsyncInternal() Log.ResolutionFailed(_logger, error, ServiceName); ExceptionDispatchInfo.Throw(error); } - else if (newEndPoints is not null) + else if (newEndpoints is not null) { - Log.ResolutionSucceeded(_logger, ServiceName, newEndPoints); + Log.ResolutionSucceeded(_logger, ServiceName, newEndpoints); } } @@ -240,9 +240,9 @@ public async ValueTask DisposeAsync() await task.ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing); } - foreach (var resolver in _resolvers) + foreach (var provider in _providers) { - await resolver.DisposeAsync().ConfigureAwait(false); + await provider.DisposeAsync().ConfigureAwait(false); } } @@ -253,14 +253,14 @@ private enum CacheStatus Valid } - private void ThrowIfNoResolvers() + private void ThrowIfNoProviders() { - if (_resolvers.Length == 0) + if (_providers.Length == 0) { - ThrowNoResolversConfigured(); + ThrowNoProvidersConfigured(); } } [DoesNotReturn] - private static void ThrowNoResolversConfigured() => throw new InvalidOperationException("No service endpoint resolvers are configured."); + private static void ThrowNoProvidersConfigured() => throw new InvalidOperationException("No service endpoint providers are configured."); } diff --git a/src/Microsoft.Extensions.ServiceDiscovery/ServiceEndPointWatcherFactory.Log.cs b/src/Microsoft.Extensions.ServiceDiscovery/ServiceEndpointWatcherFactory.Log.cs similarity index 53% rename from src/Microsoft.Extensions.ServiceDiscovery/ServiceEndPointWatcherFactory.Log.cs rename to src/Microsoft.Extensions.ServiceDiscovery/ServiceEndpointWatcherFactory.Log.cs index 69f565eb8e..5f4acc8987 100644 --- a/src/Microsoft.Extensions.ServiceDiscovery/ServiceEndPointWatcherFactory.Log.cs +++ b/src/Microsoft.Extensions.ServiceDiscovery/ServiceEndpointWatcherFactory.Log.cs @@ -5,17 +5,18 @@ namespace Microsoft.Extensions.ServiceDiscovery; -partial class ServiceEndPointWatcherFactory +partial class ServiceEndpointWatcherFactory { private sealed partial class Log { - [LoggerMessage(1, LogLevel.Debug, "Creating endpoint resolver for service '{ServiceName}' with {Count} resolvers: {Resolvers}.", EventName = "CreatingResolver")] - public static partial void ServiceEndPointResolverListCore(ILogger logger, string serviceName, int count, string resolvers); - public static void CreatingResolver(ILogger logger, string serviceName, List resolvers) + [LoggerMessage(1, LogLevel.Debug, "Creating endpoint resolver for service '{ServiceName}' with {Count} providers: {Providers}.", EventName = "CreatingResolver")] + public static partial void ServiceEndpointProviderListCore(ILogger logger, string serviceName, int count, string providers); + + public static void CreatingResolver(ILogger logger, string serviceName, List providers) { if (logger.IsEnabled(LogLevel.Debug)) { - ServiceEndPointResolverListCore(logger, serviceName, resolvers.Count, string.Join(", ", resolvers.Select(static r => r.ToString()))); + ServiceEndpointProviderListCore(logger, serviceName, providers.Count, string.Join(", ", providers.Select(static r => r.ToString()))); } } } diff --git a/src/Microsoft.Extensions.ServiceDiscovery/ServiceEndpointWatcherFactory.cs b/src/Microsoft.Extensions.ServiceDiscovery/ServiceEndpointWatcherFactory.cs new file mode 100644 index 0000000000..6cc7cb2cbc --- /dev/null +++ b/src/Microsoft.Extensions.ServiceDiscovery/ServiceEndpointWatcherFactory.cs @@ -0,0 +1,61 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Microsoft.Extensions.ServiceDiscovery.PassThrough; + +namespace Microsoft.Extensions.ServiceDiscovery; + +/// +/// Creates service endpoint watchers. +/// +internal sealed partial class ServiceEndpointWatcherFactory( + IEnumerable providerFactories, + ILogger logger, + IOptions options, + TimeProvider timeProvider) +{ + private readonly IServiceEndpointProviderFactory[] _providerFactories = providerFactories + .Where(r => r is not PassThroughServiceEndpointProviderFactory) + .Concat(providerFactories.Where(static r => r is PassThroughServiceEndpointProviderFactory)).ToArray(); + private readonly ILogger _logger = logger; + private readonly TimeProvider _timeProvider = timeProvider; + private readonly IOptions _options = options; + + /// + /// Creates a service endpoint watcher for the provided service name. + /// + public ServiceEndpointWatcher CreateWatcher(string serviceName) + { + ArgumentNullException.ThrowIfNull(serviceName); + + if (!ServiceEndpointQuery.TryParse(serviceName, out var query)) + { + throw new ArgumentException("The provided input was not in a valid format. It must be a valid URI.", nameof(serviceName)); + } + + List? providers = null; + foreach (var factory in _providerFactories) + { + if (factory.TryCreateProvider(query, out var provider)) + { + providers ??= []; + providers.Add(provider); + } + } + + if (providers is not { Count: > 0 }) + { + throw new InvalidOperationException($"No provider which supports the provided service name, '{serviceName}', has been configured."); + } + + Log.CreatingResolver(_logger, serviceName, providers); + return new ServiceEndpointWatcher( + providers: [.. providers], + logger: _logger, + serviceName: serviceName, + timeProvider: _timeProvider, + options: _options); + } +} diff --git a/tests/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/DnsSrvServiceEndPointResolverTests.cs b/tests/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/DnsSrvServiceEndpointResolverTests.cs similarity index 72% rename from tests/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/DnsSrvServiceEndPointResolverTests.cs rename to tests/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/DnsSrvServiceEndpointResolverTests.cs index 25cd88a143..7cadb4e3c7 100644 --- a/tests/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/DnsSrvServiceEndPointResolverTests.cs +++ b/tests/Microsoft.Extensions.ServiceDiscovery.Dns.Tests/DnsSrvServiceEndpointResolverTests.cs @@ -14,10 +14,10 @@ namespace Microsoft.Extensions.ServiceDiscovery.Dns.Tests; /// -/// Tests for and . -/// These also cover and by extension. +/// Tests for and . +/// These also cover and by extension. /// -public class DnsSrvServiceEndPointResolverTests +public class DnsSrvServiceEndpointResolverTests { private sealed class FakeDnsClient : IDnsQuery { @@ -73,7 +73,7 @@ private sealed class FakeDnsQueryResponse : IDnsQueryResponse } [Fact] - public async Task ResolveServiceEndPoint_Dns() + public async Task ResolveServiceEndpoint_Dns() { var dnsClientMock = new FakeDnsClient { @@ -101,26 +101,26 @@ public async Task ResolveServiceEndPoint_Dns() var services = new ServiceCollection() .AddSingleton(dnsClientMock) .AddServiceDiscoveryCore() - .AddDnsSrvServiceEndPointResolver(options => options.QuerySuffix = ".ns") + .AddDnsSrvServiceEndpointProvider(options => options.QuerySuffix = ".ns") .BuildServiceProvider(); - var resolverFactory = services.GetRequiredService(); - ServiceEndPointWatcher resolver; - await using ((resolver = resolverFactory.CreateWatcher("http://basket")).ConfigureAwait(false)) + var watcherFactory = services.GetRequiredService(); + ServiceEndpointWatcher watcher; + await using ((watcher = watcherFactory.CreateWatcher("http://basket")).ConfigureAwait(false)) { - Assert.NotNull(resolver); - var tcs = new TaskCompletionSource(); - resolver.OnEndPointsUpdated = tcs.SetResult; - resolver.Start(); + Assert.NotNull(watcher); + var tcs = new TaskCompletionSource(); + watcher.OnEndpointsUpdated = tcs.SetResult; + watcher.Start(); var initialResult = await tcs.Task.ConfigureAwait(false); Assert.NotNull(initialResult); Assert.True(initialResult.ResolvedSuccessfully); - Assert.Equal(3, initialResult.EndPointSource.EndPoints.Count); - var eps = initialResult.EndPointSource.EndPoints; + Assert.Equal(3, initialResult.EndpointSource.Endpoints.Count); + var eps = initialResult.EndpointSource.Endpoints; Assert.Equal(new IPEndPoint(IPAddress.Parse("10.10.10.10"), 8888), eps[0].EndPoint); Assert.Equal(new IPEndPoint(IPAddress.IPv6Loopback, 9999), eps[1].EndPoint); Assert.Equal(new DnsEndPoint("remotehost", 7777), eps[2].EndPoint); - Assert.All(initialResult.EndPointSource.EndPoints, ep => + Assert.All(initialResult.EndpointSource.Endpoints, ep => { var hostNameFeature = ep.Features.Get(); Assert.Null(hostNameFeature); @@ -134,7 +134,7 @@ public async Task ResolveServiceEndPoint_Dns() [InlineData(true)] [InlineData(false)] [Theory] - public async Task ResolveServiceEndPoint_Dns_MultipleProviders_PreventMixing(bool dnsFirst) + public async Task ResolveServiceEndpoint_Dns_MultipleProviders_PreventMixing(bool dnsFirst) { var dnsClientMock = new FakeDnsClient { @@ -175,28 +175,28 @@ public async Task ResolveServiceEndPoint_Dns_MultipleProviders_PreventMixing(boo if (dnsFirst) { serviceCollection - .AddDnsSrvServiceEndPointResolver(options => + .AddDnsSrvServiceEndpointProvider(options => { options.QuerySuffix = ".ns"; - options.ApplyHostNameMetadata = _ => true; + options.ShouldApplyHostNameMetadata = _ => true; }) - .AddConfigurationServiceEndPointResolver(); + .AddConfigurationServiceEndpointProvider(); } else { serviceCollection - .AddConfigurationServiceEndPointResolver() - .AddDnsSrvServiceEndPointResolver(options => options.QuerySuffix = ".ns"); + .AddConfigurationServiceEndpointProvider() + .AddDnsSrvServiceEndpointProvider(options => options.QuerySuffix = ".ns"); }; var services = serviceCollection.BuildServiceProvider(); - var resolverFactory = services.GetRequiredService(); - ServiceEndPointWatcher resolver; - await using ((resolver = resolverFactory.CreateWatcher("http://basket")).ConfigureAwait(false)) + var watcherFactory = services.GetRequiredService(); + ServiceEndpointWatcher watcher; + await using ((watcher = watcherFactory.CreateWatcher("http://basket")).ConfigureAwait(false)) { - Assert.NotNull(resolver); - var tcs = new TaskCompletionSource(); - resolver.OnEndPointsUpdated = tcs.SetResult; - resolver.Start(); + Assert.NotNull(watcher); + var tcs = new TaskCompletionSource(); + watcher.OnEndpointsUpdated = tcs.SetResult; + watcher.Start(); var initialResult = await tcs.Task.ConfigureAwait(false); Assert.NotNull(initialResult); Assert.Null(initialResult.Exception); @@ -205,13 +205,13 @@ public async Task ResolveServiceEndPoint_Dns_MultipleProviders_PreventMixing(boo if (dnsFirst) { // We expect only the results from the DNS provider. - Assert.Equal(3, initialResult.EndPointSource.EndPoints.Count); - var eps = initialResult.EndPointSource.EndPoints; + Assert.Equal(3, initialResult.EndpointSource.Endpoints.Count); + var eps = initialResult.EndpointSource.Endpoints; Assert.Equal(new IPEndPoint(IPAddress.Parse("10.10.10.10"), 8888), eps[0].EndPoint); Assert.Equal(new IPEndPoint(IPAddress.IPv6Loopback, 9999), eps[1].EndPoint); Assert.Equal(new DnsEndPoint("remotehost", 7777), eps[2].EndPoint); - Assert.All(initialResult.EndPointSource.EndPoints, ep => + Assert.All(initialResult.EndpointSource.Endpoints, ep => { var hostNameFeature = ep.Features.Get(); Assert.NotNull(hostNameFeature); @@ -221,11 +221,11 @@ public async Task ResolveServiceEndPoint_Dns_MultipleProviders_PreventMixing(boo else { // We expect only the results from the Configuration provider. - Assert.Equal(2, initialResult.EndPointSource.EndPoints.Count); - Assert.Equal(new DnsEndPoint("localhost", 8080), initialResult.EndPointSource.EndPoints[0].EndPoint); - Assert.Equal(new DnsEndPoint("remotehost", 9090), initialResult.EndPointSource.EndPoints[1].EndPoint); + Assert.Equal(2, initialResult.EndpointSource.Endpoints.Count); + Assert.Equal(new DnsEndPoint("localhost", 8080), initialResult.EndpointSource.Endpoints[0].EndPoint); + Assert.Equal(new DnsEndPoint("remotehost", 9090), initialResult.EndpointSource.Endpoints[1].EndPoint); - Assert.All(initialResult.EndPointSource.EndPoints, ep => + Assert.All(initialResult.EndpointSource.Endpoints, ep => { var hostNameFeature = ep.Features.Get(); Assert.Null(hostNameFeature); @@ -248,59 +248,4 @@ public void SetValues(IEnumerable> values) OnReload(); } } - - /* - [Fact] - public async Task ResolveServiceEndPoint_Dns_RespectsChangeToken() - { - var oneEndPoint = new Dictionary - { - ["services:basket:http:0:host"] = "localhost", - ["services:basket:http:0:port"] = "8080", - }; - var bothEndPoints = new Dictionary(oneEndPoint) - { - ["services:basket:http:1:host"] = "remotehost", - ["services:basket:http:1:port"] = "9090", - }; - var configSource = new MyConfigurationProvider(); - var services = new ServiceCollection() - .AddSingleton(new ConfigurationBuilder().Add(configSource).Build()) - .AddServiceDiscovery() - .AddConfigurationServiceEndPointResolver() - .BuildServiceProvider(); - var resolverFactory = services.GetRequiredService(); - ServiceEndPointResolver resolver; - await using ((resolver = resolverFactory.CreateWatcher("http://basket")).ConfigureAwait(false)) - { - Assert.NotNull(resolver); - var channel = Channel.CreateUnbounded(); - resolver.OnEndPointsUpdated = v => channel.Writer.TryWrite(v); - resolver.Start(); - var initialResult = await channel.Reader.ReadAsync(CancellationToken.None).ConfigureAwait(false); - Assert.NotNull(initialResult); - Assert.False(initialResult.ResolvedSuccessfully); - Assert.Equal(ResolutionStatusCode.Error, initialResult.Status.StatusCode); - Assert.Null(initialResult.EndPoints); - - // Update the config and check that it flows through the system. - configSource.SetValues(oneEndPoint); - - // If we don't get an update relatively soon, something is broken. We add a timeout here because we don't want an issue to - // cause an indefinite test hang. We expect the result to be published practically immediately, though. - _ = await channel.Reader.ReadAsync(CancellationToken.None).AsTask().WaitAsync(TimeSpan.FromSeconds(30)).ConfigureAwait(false); - var oneEpResult = await resolver.GetEndPointsAsync(CancellationToken.None).ConfigureAwait(false); - var firstEp = Assert.Single(oneEpResult); - Assert.Equal(new DnsEndPoint("localhost", 8080), firstEp.EndPoint); - - // Do it again to check that an updated (not cached) version is published. - configSource.SetValues(bothEndPoints); - var twoEpResult = await channel.Reader.ReadAsync(CancellationToken.None).ConfigureAwait(false); - Assert.True(twoEpResult.ResolvedSuccessfully); - Assert.Equal(2, twoEpResult.EndPoints.Count); - Assert.Equal(new DnsEndPoint("localhost", 8080), twoEpResult.EndPoints[0].EndPoint); - Assert.Equal(new DnsEndPoint("remotehost", 9090), twoEpResult.EndPoints[1].EndPoint); - } - } - */ } diff --git a/tests/Microsoft.Extensions.ServiceDiscovery.Tests/ConfigurationServiceEndPointResolverTests.cs b/tests/Microsoft.Extensions.ServiceDiscovery.Tests/ConfigurationServiceEndpointResolverTests.cs similarity index 60% rename from tests/Microsoft.Extensions.ServiceDiscovery.Tests/ConfigurationServiceEndPointResolverTests.cs rename to tests/Microsoft.Extensions.ServiceDiscovery.Tests/ConfigurationServiceEndpointResolverTests.cs index 6d8091f026..db72078210 100644 --- a/tests/Microsoft.Extensions.ServiceDiscovery.Tests/ConfigurationServiceEndPointResolverTests.cs +++ b/tests/Microsoft.Extensions.ServiceDiscovery.Tests/ConfigurationServiceEndpointResolverTests.cs @@ -12,13 +12,13 @@ namespace Microsoft.Extensions.ServiceDiscovery.Tests; /// -/// Tests for . -/// These also cover and by extension. +/// Tests for . +/// These also cover and by extension. /// -public class ConfigurationServiceEndPointResolverTests +public class ConfigurationServiceEndpointResolverTests { [Fact] - public async Task ResolveServiceEndPoint_Configuration_SingleResult() + public async Task ResolveServiceEndpoint_Configuration_SingleResult() { var config = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary { @@ -27,23 +27,23 @@ public async Task ResolveServiceEndPoint_Configuration_SingleResult() var services = new ServiceCollection() .AddSingleton(config.Build()) .AddServiceDiscoveryCore() - .AddConfigurationServiceEndPointResolver() + .AddConfigurationServiceEndpointProvider() .BuildServiceProvider(); - var resolverFactory = services.GetRequiredService(); - ServiceEndPointWatcher resolver; - await using ((resolver = resolverFactory.CreateWatcher("http://basket")).ConfigureAwait(false)) + var watcherFactory = services.GetRequiredService(); + ServiceEndpointWatcher watcher; + await using ((watcher = watcherFactory.CreateWatcher("http://basket")).ConfigureAwait(false)) { - Assert.NotNull(resolver); - var tcs = new TaskCompletionSource(); - resolver.OnEndPointsUpdated = tcs.SetResult; - resolver.Start(); + Assert.NotNull(watcher); + var tcs = new TaskCompletionSource(); + watcher.OnEndpointsUpdated = tcs.SetResult; + watcher.Start(); var initialResult = await tcs.Task.ConfigureAwait(false); Assert.NotNull(initialResult); Assert.True(initialResult.ResolvedSuccessfully); - var ep = Assert.Single(initialResult.EndPointSource.EndPoints); + var ep = Assert.Single(initialResult.EndpointSource.Endpoints); Assert.Equal(new DnsEndPoint("localhost", 8080), ep.EndPoint); - Assert.All(initialResult.EndPointSource.EndPoints, ep => + Assert.All(initialResult.EndpointSource.Endpoints, ep => { var hostNameFeature = ep.Features.Get(); Assert.Null(hostNameFeature); @@ -52,7 +52,7 @@ public async Task ResolveServiceEndPoint_Configuration_SingleResult() } [Fact] - public async Task ResolveServiceEndPoint_Configuration_DisallowedScheme() + public async Task ResolveServiceEndpoint_Configuration_DisallowedScheme() { // Try to resolve an http endpoint when only https is allowed. var config = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary @@ -63,59 +63,63 @@ public async Task ResolveServiceEndPoint_Configuration_DisallowedScheme() var services = new ServiceCollection() .AddSingleton(config.Build()) .AddServiceDiscoveryCore() - .AddConfigurationServiceEndPointResolver() - .Configure(o => o.AllowedSchemes = ["https"]) + .AddConfigurationServiceEndpointProvider() + .Configure(o => + { + o.AllowAllSchemes = false; + o.AllowedSchemes = ["https"]; + }) .BuildServiceProvider(); - var resolverFactory = services.GetRequiredService(); - ServiceEndPointWatcher resolver; + var watcherFactory = services.GetRequiredService(); + ServiceEndpointWatcher watcher; // Explicitly specifying http. // We should get no endpoint back because http is not allowed by configuration. - await using ((resolver = resolverFactory.CreateWatcher("http://_foo.basket")).ConfigureAwait(false)) + await using ((watcher = watcherFactory.CreateWatcher("http://_foo.basket")).ConfigureAwait(false)) { - Assert.NotNull(resolver); - var tcs = new TaskCompletionSource(); - resolver.OnEndPointsUpdated = tcs.SetResult; - resolver.Start(); + Assert.NotNull(watcher); + var tcs = new TaskCompletionSource(); + watcher.OnEndpointsUpdated = tcs.SetResult; + watcher.Start(); var initialResult = await tcs.Task.ConfigureAwait(false); Assert.NotNull(initialResult); Assert.True(initialResult.ResolvedSuccessfully); - Assert.Empty(initialResult.EndPointSource.EndPoints); + Assert.Empty(initialResult.EndpointSource.Endpoints); } // Specifying either https or http. // The result should be that we only get the http endpoint back. - await using ((resolver = resolverFactory.CreateWatcher("https+http://_foo.basket")).ConfigureAwait(false)) + await using ((watcher = watcherFactory.CreateWatcher("https+http://_foo.basket")).ConfigureAwait(false)) { - Assert.NotNull(resolver); - var tcs = new TaskCompletionSource(); - resolver.OnEndPointsUpdated = tcs.SetResult; - resolver.Start(); + Assert.NotNull(watcher); + var tcs = new TaskCompletionSource(); + watcher.OnEndpointsUpdated = tcs.SetResult; + watcher.Start(); var initialResult = await tcs.Task.ConfigureAwait(false); Assert.NotNull(initialResult); Assert.True(initialResult.ResolvedSuccessfully); - var ep = Assert.Single(initialResult.EndPointSource.EndPoints); + var ep = Assert.Single(initialResult.EndpointSource.Endpoints); Assert.Equal(new UriEndPoint(new Uri("https://localhost")), ep.EndPoint); } // Specifying either https or http, but in reverse. // The result should be that we only get the http endpoint back. - await using ((resolver = resolverFactory.CreateWatcher("http+https://_foo.basket")).ConfigureAwait(false)) + await using ((watcher = watcherFactory.CreateWatcher("http+https://_foo.basket")).ConfigureAwait(false)) { - Assert.NotNull(resolver); - var tcs = new TaskCompletionSource(); - resolver.OnEndPointsUpdated = tcs.SetResult; - resolver.Start(); + Assert.NotNull(watcher); + var tcs = new TaskCompletionSource(); + watcher.OnEndpointsUpdated = tcs.SetResult; + watcher.Start(); var initialResult = await tcs.Task.ConfigureAwait(false); Assert.NotNull(initialResult); Assert.True(initialResult.ResolvedSuccessfully); - var ep = Assert.Single(initialResult.EndPointSource.EndPoints); + var ep = Assert.Single(initialResult.EndpointSource.Endpoints); Assert.Equal(new UriEndPoint(new Uri("https://localhost")), ep.EndPoint); } } [Fact] - public async Task ResolveServiceEndPoint_Configuration_MultipleResults() + public async Task ResolveServiceEndpoint_Configuration_MultipleResults() { var configSource = new MemoryConfigurationSource { @@ -129,24 +133,24 @@ public async Task ResolveServiceEndPoint_Configuration_MultipleResults() var services = new ServiceCollection() .AddSingleton(config.Build()) .AddServiceDiscoveryCore() - .AddConfigurationServiceEndPointResolver(options => options.ApplyHostNameMetadata = _ => true) + .AddConfigurationServiceEndpointProvider(options => options.ShouldApplyHostNameMetadata = _ => true) .BuildServiceProvider(); - var resolverFactory = services.GetRequiredService(); - ServiceEndPointWatcher resolver; - await using ((resolver = resolverFactory.CreateWatcher("http://basket")).ConfigureAwait(false)) + var watcherFactory = services.GetRequiredService(); + ServiceEndpointWatcher watcher; + await using ((watcher = watcherFactory.CreateWatcher("http://basket")).ConfigureAwait(false)) { - Assert.NotNull(resolver); - var tcs = new TaskCompletionSource(); - resolver.OnEndPointsUpdated = tcs.SetResult; - resolver.Start(); + Assert.NotNull(watcher); + var tcs = new TaskCompletionSource(); + watcher.OnEndpointsUpdated = tcs.SetResult; + watcher.Start(); var initialResult = await tcs.Task.ConfigureAwait(false); Assert.NotNull(initialResult); Assert.True(initialResult.ResolvedSuccessfully); - Assert.Equal(2, initialResult.EndPointSource.EndPoints.Count); - Assert.Equal(new UriEndPoint(new Uri("http://localhost:8080")), initialResult.EndPointSource.EndPoints[0].EndPoint); - Assert.Equal(new UriEndPoint(new Uri("http://remotehost:9090")), initialResult.EndPointSource.EndPoints[1].EndPoint); + Assert.Equal(2, initialResult.EndpointSource.Endpoints.Count); + Assert.Equal(new UriEndPoint(new Uri("http://localhost:8080")), initialResult.EndpointSource.Endpoints[0].EndPoint); + Assert.Equal(new UriEndPoint(new Uri("http://remotehost:9090")), initialResult.EndpointSource.Endpoints[1].EndPoint); - Assert.All(initialResult.EndPointSource.EndPoints, ep => + Assert.All(initialResult.EndpointSource.Endpoints, ep => { var hostNameFeature = ep.Features.Get(); Assert.NotNull(hostNameFeature); @@ -155,20 +159,20 @@ public async Task ResolveServiceEndPoint_Configuration_MultipleResults() } // Request either https or http. Since there are only http endpoints, we should get only http endpoints back. - await using ((resolver = resolverFactory.CreateWatcher("https+http://basket")).ConfigureAwait(false)) + await using ((watcher = watcherFactory.CreateWatcher("https+http://basket")).ConfigureAwait(false)) { - Assert.NotNull(resolver); - var tcs = new TaskCompletionSource(); - resolver.OnEndPointsUpdated = tcs.SetResult; - resolver.Start(); + Assert.NotNull(watcher); + var tcs = new TaskCompletionSource(); + watcher.OnEndpointsUpdated = tcs.SetResult; + watcher.Start(); var initialResult = await tcs.Task.ConfigureAwait(false); Assert.NotNull(initialResult); Assert.True(initialResult.ResolvedSuccessfully); - Assert.Equal(2, initialResult.EndPointSource.EndPoints.Count); - Assert.Equal(new UriEndPoint(new Uri("http://localhost:8080")), initialResult.EndPointSource.EndPoints[0].EndPoint); - Assert.Equal(new UriEndPoint(new Uri("http://remotehost:9090")), initialResult.EndPointSource.EndPoints[1].EndPoint); + Assert.Equal(2, initialResult.EndpointSource.Endpoints.Count); + Assert.Equal(new UriEndPoint(new Uri("http://localhost:8080")), initialResult.EndpointSource.Endpoints[0].EndPoint); + Assert.Equal(new UriEndPoint(new Uri("http://remotehost:9090")), initialResult.EndpointSource.Endpoints[1].EndPoint); - Assert.All(initialResult.EndPointSource.EndPoints, ep => + Assert.All(initialResult.EndpointSource.Endpoints, ep => { var hostNameFeature = ep.Features.Get(); Assert.NotNull(hostNameFeature); @@ -178,7 +182,7 @@ public async Task ResolveServiceEndPoint_Configuration_MultipleResults() } [Fact] - public async Task ResolveServiceEndPoint_Configuration_MultipleProtocols() + public async Task ResolveServiceEndpoint_Configuration_MultipleProtocols() { var configSource = new MemoryConfigurationSource { @@ -196,25 +200,25 @@ public async Task ResolveServiceEndPoint_Configuration_MultipleProtocols() var services = new ServiceCollection() .AddSingleton(config.Build()) .AddServiceDiscoveryCore() - .AddConfigurationServiceEndPointResolver() + .AddConfigurationServiceEndpointProvider() .BuildServiceProvider(); - var resolverFactory = services.GetRequiredService(); - ServiceEndPointWatcher resolver; - await using ((resolver = resolverFactory.CreateWatcher("http://_grpc.basket")).ConfigureAwait(false)) + var watcherFactory = services.GetRequiredService(); + ServiceEndpointWatcher watcher; + await using ((watcher = watcherFactory.CreateWatcher("http://_grpc.basket")).ConfigureAwait(false)) { - Assert.NotNull(resolver); - var tcs = new TaskCompletionSource(); - resolver.OnEndPointsUpdated = tcs.SetResult; - resolver.Start(); + Assert.NotNull(watcher); + var tcs = new TaskCompletionSource(); + watcher.OnEndpointsUpdated = tcs.SetResult; + watcher.Start(); var initialResult = await tcs.Task.ConfigureAwait(false); Assert.NotNull(initialResult); Assert.True(initialResult.ResolvedSuccessfully); - Assert.Equal(3, initialResult.EndPointSource.EndPoints.Count); - Assert.Equal(new DnsEndPoint("localhost", 2222), initialResult.EndPointSource.EndPoints[0].EndPoint); - Assert.Equal(new IPEndPoint(IPAddress.Loopback, 3333), initialResult.EndPointSource.EndPoints[1].EndPoint); - Assert.Equal(new UriEndPoint(new Uri("http://remotehost:4444")), initialResult.EndPointSource.EndPoints[2].EndPoint); + Assert.Equal(3, initialResult.EndpointSource.Endpoints.Count); + Assert.Equal(new DnsEndPoint("localhost", 2222), initialResult.EndpointSource.Endpoints[0].EndPoint); + Assert.Equal(new IPEndPoint(IPAddress.Loopback, 3333), initialResult.EndpointSource.Endpoints[1].EndPoint); + Assert.Equal(new UriEndPoint(new Uri("http://remotehost:4444")), initialResult.EndpointSource.Endpoints[2].EndPoint); - Assert.All(initialResult.EndPointSource.EndPoints, ep => + Assert.All(initialResult.EndpointSource.Endpoints, ep => { var hostNameFeature = ep.Features.Get(); Assert.Null(hostNameFeature); @@ -223,7 +227,7 @@ public async Task ResolveServiceEndPoint_Configuration_MultipleProtocols() } [Fact] - public async Task ResolveServiceEndPoint_Configuration_MultipleProtocols_WithSpecificationByConsumer() + public async Task ResolveServiceEndpoint_Configuration_MultipleProtocols_WithSpecificationByConsumer() { var configSource = new MemoryConfigurationSource { @@ -241,29 +245,29 @@ public async Task ResolveServiceEndPoint_Configuration_MultipleProtocols_WithSpe var services = new ServiceCollection() .AddSingleton(config.Build()) .AddServiceDiscoveryCore() - .AddConfigurationServiceEndPointResolver() + .AddConfigurationServiceEndpointProvider() .BuildServiceProvider(); - var resolverFactory = services.GetRequiredService(); - ServiceEndPointWatcher resolver; - await using ((resolver = resolverFactory.CreateWatcher("https+http://_grpc.basket")).ConfigureAwait(false)) + var watcherFactory = services.GetRequiredService(); + ServiceEndpointWatcher watcher; + await using ((watcher = watcherFactory.CreateWatcher("https+http://_grpc.basket")).ConfigureAwait(false)) { - Assert.NotNull(resolver); - var tcs = new TaskCompletionSource(); - resolver.OnEndPointsUpdated = tcs.SetResult; - resolver.Start(); + Assert.NotNull(watcher); + var tcs = new TaskCompletionSource(); + watcher.OnEndpointsUpdated = tcs.SetResult; + watcher.Start(); var initialResult = await tcs.Task.ConfigureAwait(false); Assert.NotNull(initialResult); Assert.True(initialResult.ResolvedSuccessfully); - Assert.Equal(3, initialResult.EndPointSource.EndPoints.Count); + Assert.Equal(3, initialResult.EndpointSource.Endpoints.Count); // These must be treated as HTTPS by the HttpClient middleware, but that is not the responsibility of the resolver. - Assert.Equal(new DnsEndPoint("localhost", 2222), initialResult.EndPointSource.EndPoints[0].EndPoint); - Assert.Equal(new IPEndPoint(IPAddress.Loopback, 3333), initialResult.EndPointSource.EndPoints[1].EndPoint); + Assert.Equal(new DnsEndPoint("localhost", 2222), initialResult.EndpointSource.Endpoints[0].EndPoint); + Assert.Equal(new IPEndPoint(IPAddress.Loopback, 3333), initialResult.EndpointSource.Endpoints[1].EndPoint); // We expect the HTTPS endpoint back but not the HTTP one. - Assert.Equal(new UriEndPoint(new Uri("https://remotehost:5555")), initialResult.EndPointSource.EndPoints[2].EndPoint); + Assert.Equal(new UriEndPoint(new Uri("https://remotehost:5555")), initialResult.EndpointSource.Endpoints[2].EndPoint); - Assert.All(initialResult.EndPointSource.EndPoints, ep => + Assert.All(initialResult.EndpointSource.Endpoints, ep => { var hostNameFeature = ep.Features.Get(); Assert.Null(hostNameFeature); diff --git a/tests/Microsoft.Extensions.ServiceDiscovery.Tests/PassThroughServiceEndPointResolverTests.cs b/tests/Microsoft.Extensions.ServiceDiscovery.Tests/PassThroughServiceEndpointResolverTests.cs similarity index 60% rename from tests/Microsoft.Extensions.ServiceDiscovery.Tests/PassThroughServiceEndPointResolverTests.cs rename to tests/Microsoft.Extensions.ServiceDiscovery.Tests/PassThroughServiceEndpointResolverTests.cs index 643bbfad44..e0af5c03ed 100644 --- a/tests/Microsoft.Extensions.ServiceDiscovery.Tests/PassThroughServiceEndPointResolverTests.cs +++ b/tests/Microsoft.Extensions.ServiceDiscovery.Tests/PassThroughServiceEndpointResolverTests.cs @@ -12,36 +12,36 @@ namespace Microsoft.Extensions.ServiceDiscovery.Tests; /// -/// Tests for . -/// These also cover and by extension. +/// Tests for . +/// These also cover and by extension. /// -public class PassThroughServiceEndPointResolverTests +public class PassThroughServiceEndpointResolverTests { [Fact] - public async Task ResolveServiceEndPoint_PassThrough() + public async Task ResolveServiceEndpoint_PassThrough() { var services = new ServiceCollection() .AddServiceDiscoveryCore() - .AddPassThroughServiceEndPointResolver() + .AddPassThroughServiceEndpointProvider() .BuildServiceProvider(); - var resolverFactory = services.GetRequiredService(); - ServiceEndPointWatcher resolver; - await using ((resolver = resolverFactory.CreateWatcher("http://basket")).ConfigureAwait(false)) + var watcherFactory = services.GetRequiredService(); + ServiceEndpointWatcher watcher; + await using ((watcher = watcherFactory.CreateWatcher("http://basket")).ConfigureAwait(false)) { - Assert.NotNull(resolver); - var tcs = new TaskCompletionSource(); - resolver.OnEndPointsUpdated = tcs.SetResult; - resolver.Start(); + Assert.NotNull(watcher); + var tcs = new TaskCompletionSource(); + watcher.OnEndpointsUpdated = tcs.SetResult; + watcher.Start(); var initialResult = await tcs.Task.ConfigureAwait(false); Assert.NotNull(initialResult); Assert.True(initialResult.ResolvedSuccessfully); - var ep = Assert.Single(initialResult.EndPointSource.EndPoints); + var ep = Assert.Single(initialResult.EndpointSource.Endpoints); Assert.Equal(new DnsEndPoint("basket", 80), ep.EndPoint); } } [Fact] - public async Task ResolveServiceEndPoint_Superseded() + public async Task ResolveServiceEndpoint_Superseded() { var configSource = new MemoryConfigurationSource { @@ -55,26 +55,26 @@ public async Task ResolveServiceEndPoint_Superseded() .AddSingleton(config.Build()) .AddServiceDiscovery() // Adds the configuration and pass-through providers. .BuildServiceProvider(); - var resolverFactory = services.GetRequiredService(); - ServiceEndPointWatcher resolver; - await using ((resolver = resolverFactory.CreateWatcher("http://basket")).ConfigureAwait(false)) + var watcherFactory = services.GetRequiredService(); + ServiceEndpointWatcher watcher; + await using ((watcher = watcherFactory.CreateWatcher("http://basket")).ConfigureAwait(false)) { - Assert.NotNull(resolver); - var tcs = new TaskCompletionSource(); - resolver.OnEndPointsUpdated = tcs.SetResult; - resolver.Start(); + Assert.NotNull(watcher); + var tcs = new TaskCompletionSource(); + watcher.OnEndpointsUpdated = tcs.SetResult; + watcher.Start(); var initialResult = await tcs.Task.ConfigureAwait(false); Assert.NotNull(initialResult); Assert.True(initialResult.ResolvedSuccessfully); // We expect the basket service to be resolved from Configuration, not the pass-through provider. - Assert.Single(initialResult.EndPointSource.EndPoints); - Assert.Equal(new UriEndPoint(new Uri("http://localhost:8080")), initialResult.EndPointSource.EndPoints[0].EndPoint); + Assert.Single(initialResult.EndpointSource.Endpoints); + Assert.Equal(new UriEndPoint(new Uri("http://localhost:8080")), initialResult.EndpointSource.Endpoints[0].EndPoint); } } [Fact] - public async Task ResolveServiceEndPoint_Fallback() + public async Task ResolveServiceEndpoint_Fallback() { var configSource = new MemoryConfigurationSource { @@ -88,27 +88,27 @@ public async Task ResolveServiceEndPoint_Fallback() .AddSingleton(config.Build()) .AddServiceDiscovery() // Adds the configuration and pass-through providers. .BuildServiceProvider(); - var resolverFactory = services.GetRequiredService(); - ServiceEndPointWatcher resolver; - await using ((resolver = resolverFactory.CreateWatcher("http://catalog")).ConfigureAwait(false)) + var watcherFactory = services.GetRequiredService(); + ServiceEndpointWatcher watcher; + await using ((watcher = watcherFactory.CreateWatcher("http://catalog")).ConfigureAwait(false)) { - Assert.NotNull(resolver); - var tcs = new TaskCompletionSource(); - resolver.OnEndPointsUpdated = tcs.SetResult; - resolver.Start(); + Assert.NotNull(watcher); + var tcs = new TaskCompletionSource(); + watcher.OnEndpointsUpdated = tcs.SetResult; + watcher.Start(); var initialResult = await tcs.Task.ConfigureAwait(false); Assert.NotNull(initialResult); Assert.True(initialResult.ResolvedSuccessfully); // We expect the CATALOG service to be resolved from the pass-through provider. - Assert.Single(initialResult.EndPointSource.EndPoints); - Assert.Equal(new DnsEndPoint("catalog", 80), initialResult.EndPointSource.EndPoints[0].EndPoint); + Assert.Single(initialResult.EndpointSource.Endpoints); + Assert.Equal(new DnsEndPoint("catalog", 80), initialResult.EndpointSource.Endpoints[0].EndPoint); } } // Ensures that pass-through resolution succeeds in scenarios where no scheme is specified during resolution. [Fact] - public async Task ResolveServiceEndPoint_Fallback_NoScheme() + public async Task ResolveServiceEndpoint_Fallback_NoScheme() { var configSource = new MemoryConfigurationSource { @@ -123,8 +123,8 @@ public async Task ResolveServiceEndPoint_Fallback_NoScheme() .AddServiceDiscovery() // Adds the configuration and pass-through providers. .BuildServiceProvider(); - var resolver = services.GetRequiredService(); - var result = await resolver.GetEndPointsAsync("catalog", default); - Assert.Equal(new DnsEndPoint("catalog", 0), result.EndPoints[0].EndPoint); + var resolver = services.GetRequiredService(); + var result = await resolver.GetEndpointsAsync("catalog", default); + Assert.Equal(new DnsEndPoint("catalog", 0), result.Endpoints[0].EndPoint); } } diff --git a/tests/Microsoft.Extensions.ServiceDiscovery.Tests/ServiceEndPointResolverTests.cs b/tests/Microsoft.Extensions.ServiceDiscovery.Tests/ServiceEndpointResolverTests.cs similarity index 62% rename from tests/Microsoft.Extensions.ServiceDiscovery.Tests/ServiceEndPointResolverTests.cs rename to tests/Microsoft.Extensions.ServiceDiscovery.Tests/ServiceEndpointResolverTests.cs index f5e506a9b7..16950e6737 100644 --- a/tests/Microsoft.Extensions.ServiceDiscovery.Tests/ServiceEndPointResolverTests.cs +++ b/tests/Microsoft.Extensions.ServiceDiscovery.Tests/ServiceEndpointResolverTests.cs @@ -15,58 +15,58 @@ namespace Microsoft.Extensions.ServiceDiscovery.Tests; /// -/// Tests for and . +/// Tests for and . /// -public class ServiceEndPointResolverTests +public class ServiceEndpointResolverTests { [Fact] - public void ResolveServiceEndPoint_NoResolversConfigured_Throws() + public void ResolveServiceEndpoint_NoProvidersConfigured_Throws() { var services = new ServiceCollection() .AddServiceDiscoveryCore() .BuildServiceProvider(); - var resolverFactory = services.GetRequiredService(); + var resolverFactory = services.GetRequiredService(); var exception = Assert.Throws(() => resolverFactory.CreateWatcher("https://basket")); - Assert.Equal("No resolver which supports the provided service name, 'https://basket', has been configured.", exception.Message); + Assert.Equal("No provider which supports the provided service name, 'https://basket', has been configured.", exception.Message); } [Fact] - public async Task ServiceEndPointResolver_NoResolversConfigured_Throws() + public async Task ServiceEndpointResolver_NoProvidersConfigured_Throws() { var services = new ServiceCollection() .AddServiceDiscoveryCore() .BuildServiceProvider(); - var resolverFactory = new ServiceEndPointWatcher([], NullLogger.Instance, "foo", TimeProvider.System, Options.Options.Create(new ServiceDiscoveryOptions())); - var exception = Assert.Throws(resolverFactory.Start); - Assert.Equal("No service endpoint resolvers are configured.", exception.Message); - exception = await Assert.ThrowsAsync(async () => await resolverFactory.GetEndPointsAsync()); - Assert.Equal("No service endpoint resolvers are configured.", exception.Message); + var watcher = new ServiceEndpointWatcher([], NullLogger.Instance, "foo", TimeProvider.System, Options.Options.Create(new ServiceDiscoveryOptions())); + var exception = Assert.Throws(watcher.Start); + Assert.Equal("No service endpoint providers are configured.", exception.Message); + exception = await Assert.ThrowsAsync(async () => await watcher.GetEndpointsAsync()); + Assert.Equal("No service endpoint providers are configured.", exception.Message); } [Fact] - public void ResolveServiceEndPoint_NullServiceName_Throws() + public void ResolveServiceEndpoint_NullServiceName_Throws() { var services = new ServiceCollection() .AddServiceDiscoveryCore() .BuildServiceProvider(); - var resolverFactory = services.GetRequiredService(); + var resolverFactory = services.GetRequiredService(); Assert.Throws(() => resolverFactory.CreateWatcher(null!)); } [Fact] - public async Task AddServiceDiscovery_NoResolvers_Throws() + public async Task AddServiceDiscovery_NoProviders_Throws() { var serviceCollection = new ServiceCollection(); serviceCollection.AddHttpClient("foo", c => c.BaseAddress = new("http://foo")).AddServiceDiscovery(); var services = serviceCollection.BuildServiceProvider(); var client = services.GetRequiredService().CreateClient("foo"); var exception = await Assert.ThrowsAsync(async () => await client.GetStringAsync("/")); - Assert.Equal("No resolver which supports the provided service name, 'http://foo', has been configured.", exception.Message); + Assert.Equal("No provider which supports the provided service name, 'http://foo', has been configured.", exception.Message); } - private sealed class FakeEndPointResolverProvider(Func createResolverDelegate) : IServiceEndPointProviderFactory + private sealed class FakeEndpointResolverProvider(Func createResolverDelegate) : IServiceEndpointProviderFactory { - public bool TryCreateProvider(ServiceEndPointQuery query, [NotNullWhen(true)] out IServiceEndPointProvider? resolver) + public bool TryCreateProvider(ServiceEndpointQuery query, [NotNullWhen(true)] out IServiceEndpointProvider? resolver) { bool result; (result, resolver) = createResolverDelegate(query); @@ -74,58 +74,58 @@ public bool TryCreateProvider(ServiceEndPointQuery query, [NotNullWhen(true)] ou } } - private sealed class FakeEndPointResolver(Func resolveAsync, Func disposeAsync) : IServiceEndPointProvider + private sealed class FakeEndpointResolver(Func resolveAsync, Func disposeAsync) : IServiceEndpointProvider { - public ValueTask PopulateAsync(IServiceEndPointBuilder endPoints, CancellationToken cancellationToken) => resolveAsync(endPoints, cancellationToken); + public ValueTask PopulateAsync(IServiceEndpointBuilder endpoints, CancellationToken cancellationToken) => resolveAsync(endpoints, cancellationToken); public ValueTask DisposeAsync() => disposeAsync(); } [Fact] - public async Task ResolveServiceEndPoint() + public async Task ResolveServiceEndpoint() { var cts = new[] { new CancellationTokenSource() }; - var innerResolver = new FakeEndPointResolver( + var innerResolver = new FakeEndpointResolver( resolveAsync: (collection, ct) => { collection.AddChangeToken(new CancellationChangeToken(cts[0].Token)); - collection.EndPoints.Add(ServiceEndPoint.Create(new IPEndPoint(IPAddress.Parse("127.1.1.1"), 8080))); + collection.Endpoints.Add(ServiceEndpoint.Create(new IPEndPoint(IPAddress.Parse("127.1.1.1"), 8080))); if (cts[0].Token.IsCancellationRequested) { cts[0] = new(); - collection.EndPoints.Add(ServiceEndPoint.Create(new IPEndPoint(IPAddress.Parse("127.1.1.2"), 8888))); + collection.Endpoints.Add(ServiceEndpoint.Create(new IPEndPoint(IPAddress.Parse("127.1.1.2"), 8888))); } return default; }, disposeAsync: () => default); - var resolverProvider = new FakeEndPointResolverProvider(name => (true, innerResolver)); + var resolverProvider = new FakeEndpointResolverProvider(name => (true, innerResolver)); var services = new ServiceCollection() - .AddSingleton(resolverProvider) + .AddSingleton(resolverProvider) .AddServiceDiscoveryCore() .BuildServiceProvider(); - var resolverFactory = services.GetRequiredService(); + var watcherFactory = services.GetRequiredService(); - ServiceEndPointWatcher resolver; - await using ((resolver = resolverFactory.CreateWatcher("http://basket")).ConfigureAwait(false)) + ServiceEndpointWatcher watcher; + await using ((watcher = watcherFactory.CreateWatcher("http://basket")).ConfigureAwait(false)) { - Assert.NotNull(resolver); - var initialResult = await resolver.GetEndPointsAsync(CancellationToken.None).ConfigureAwait(false); + Assert.NotNull(watcher); + var initialResult = await watcher.GetEndpointsAsync(CancellationToken.None).ConfigureAwait(false); Assert.NotNull(initialResult); - var sep = Assert.Single(initialResult.EndPoints); + var sep = Assert.Single(initialResult.Endpoints); var ip = Assert.IsType(sep.EndPoint); Assert.Equal(IPAddress.Parse("127.1.1.1"), ip.Address); Assert.Equal(8080, ip.Port); - var tcs = new TaskCompletionSource(); - resolver.OnEndPointsUpdated = tcs.SetResult; + var tcs = new TaskCompletionSource(); + watcher.OnEndpointsUpdated = tcs.SetResult; Assert.False(tcs.Task.IsCompleted); cts[0].Cancel(); var resolverResult = await tcs.Task.ConfigureAwait(false); Assert.NotNull(resolverResult); Assert.True(resolverResult.ResolvedSuccessfully); - Assert.Equal(2, resolverResult.EndPointSource.EndPoints.Count); - var endpoints = resolverResult.EndPointSource.EndPoints.Select(ep => ep.EndPoint).OfType().ToList(); + Assert.Equal(2, resolverResult.EndpointSource.Endpoints.Count); + var endpoints = resolverResult.EndpointSource.Endpoints.Select(ep => ep.EndPoint).OfType().ToList(); endpoints.Sort((l, r) => l.Port - r.Port); Assert.Equal(new IPEndPoint(IPAddress.Parse("127.1.1.1"), 8080), endpoints[0]); Assert.Equal(new IPEndPoint(IPAddress.Parse("127.1.1.2"), 8888), endpoints[1]); @@ -133,34 +133,34 @@ public async Task ResolveServiceEndPoint() } [Fact] - public async Task ResolveServiceEndPointOneShot() + public async Task ResolveServiceEndpointOneShot() { var cts = new[] { new CancellationTokenSource() }; - var innerResolver = new FakeEndPointResolver( + var innerResolver = new FakeEndpointResolver( resolveAsync: (collection, ct) => { collection.AddChangeToken(new CancellationChangeToken(cts[0].Token)); - collection.EndPoints.Add(ServiceEndPoint.Create(new IPEndPoint(IPAddress.Parse("127.1.1.1"), 8080))); + collection.Endpoints.Add(ServiceEndpoint.Create(new IPEndPoint(IPAddress.Parse("127.1.1.1"), 8080))); if (cts[0].Token.IsCancellationRequested) { cts[0] = new(); - collection.EndPoints.Add(ServiceEndPoint.Create(new IPEndPoint(IPAddress.Parse("127.1.1.2"), 8888))); + collection.Endpoints.Add(ServiceEndpoint.Create(new IPEndPoint(IPAddress.Parse("127.1.1.2"), 8888))); } return default; }, disposeAsync: () => default); - var resolverProvider = new FakeEndPointResolverProvider(name => (true, innerResolver)); + var resolverProvider = new FakeEndpointResolverProvider(name => (true, innerResolver)); var services = new ServiceCollection() - .AddSingleton(resolverProvider) + .AddSingleton(resolverProvider) .AddServiceDiscoveryCore() .BuildServiceProvider(); - var resolver = services.GetRequiredService(); + var resolver = services.GetRequiredService(); Assert.NotNull(resolver); - var initialResult = await resolver.GetEndPointsAsync("http://basket", CancellationToken.None).ConfigureAwait(false); + var initialResult = await resolver.GetEndpointsAsync("http://basket", CancellationToken.None).ConfigureAwait(false); Assert.NotNull(initialResult); - var sep = Assert.Single(initialResult.EndPoints); + var sep = Assert.Single(initialResult.Endpoints); var ip = Assert.IsType(sep.EndPoint); Assert.Equal(IPAddress.Parse("127.1.1.1"), ip.Address); Assert.Equal(8080, ip.Port); @@ -169,36 +169,36 @@ public async Task ResolveServiceEndPointOneShot() } [Fact] - public async Task ResolveHttpServiceEndPointOneShot() + public async Task ResolveHttpServiceEndpointOneShot() { var cts = new[] { new CancellationTokenSource() }; - var innerResolver = new FakeEndPointResolver( + var innerResolver = new FakeEndpointResolver( resolveAsync: (collection, ct) => { collection.AddChangeToken(new CancellationChangeToken(cts[0].Token)); - collection.EndPoints.Add(ServiceEndPoint.Create(new IPEndPoint(IPAddress.Parse("127.1.1.1"), 8080))); + collection.Endpoints.Add(ServiceEndpoint.Create(new IPEndPoint(IPAddress.Parse("127.1.1.1"), 8080))); if (cts[0].Token.IsCancellationRequested) { cts[0] = new(); - collection.EndPoints.Add(ServiceEndPoint.Create(new IPEndPoint(IPAddress.Parse("127.1.1.2"), 8888))); + collection.Endpoints.Add(ServiceEndpoint.Create(new IPEndPoint(IPAddress.Parse("127.1.1.2"), 8888))); } return default; }, disposeAsync: () => default); - var fakeResolverProvider = new FakeEndPointResolverProvider(name => (true, innerResolver)); + var fakeResolverProvider = new FakeEndpointResolverProvider(name => (true, innerResolver)); var services = new ServiceCollection() - .AddSingleton(fakeResolverProvider) + .AddSingleton(fakeResolverProvider) .AddServiceDiscoveryCore() .BuildServiceProvider(); - var resolverProvider = services.GetRequiredService(); - await using var resolver = new HttpServiceEndPointResolver(resolverProvider, services, TimeProvider.System); + var resolverProvider = services.GetRequiredService(); + await using var resolver = new HttpServiceEndpointResolver(resolverProvider, services, TimeProvider.System); Assert.NotNull(resolver); var httpRequest = new HttpRequestMessage(HttpMethod.Get, "http://basket"); - var endPoint = await resolver.GetEndpointAsync(httpRequest, CancellationToken.None).ConfigureAwait(false); - Assert.NotNull(endPoint); - var ip = Assert.IsType(endPoint.EndPoint); + var endpoint = await resolver.GetEndpointAsync(httpRequest, CancellationToken.None).ConfigureAwait(false); + Assert.NotNull(endpoint); + var ip = Assert.IsType(endpoint.EndPoint); Assert.Equal(IPAddress.Parse("127.1.1.1"), ip.Address); Assert.Equal(8080, ip.Port); @@ -206,12 +206,12 @@ public async Task ResolveHttpServiceEndPointOneShot() } [Fact] - public async Task ResolveServiceEndPoint_ThrowOnReload() + public async Task ResolveServiceEndpoint_ThrowOnReload() { var sem = new SemaphoreSlim(0); var cts = new[] { new CancellationTokenSource() }; var throwOnNextResolve = new[] { false }; - var innerResolver = new FakeEndPointResolver( + var innerResolver = new FakeEndpointResolver( resolveAsync: async (collection, ct) => { await sem.WaitAsync(ct).ConfigureAwait(false); @@ -228,25 +228,25 @@ public async Task ResolveServiceEndPoint_ThrowOnReload() } collection.AddChangeToken(new CancellationChangeToken(cts[0].Token)); - collection.EndPoints.Add(ServiceEndPoint.Create(new IPEndPoint(IPAddress.Parse("127.1.1.1"), 8080))); + collection.Endpoints.Add(ServiceEndpoint.Create(new IPEndPoint(IPAddress.Parse("127.1.1.1"), 8080))); }, disposeAsync: () => default); - var resolverProvider = new FakeEndPointResolverProvider(name => (true, innerResolver)); + var resolverProvider = new FakeEndpointResolverProvider(name => (true, innerResolver)); var services = new ServiceCollection() - .AddSingleton(resolverProvider) + .AddSingleton(resolverProvider) .AddServiceDiscoveryCore() .BuildServiceProvider(); - var resolverFactory = services.GetRequiredService(); + var watcherFactory = services.GetRequiredService(); - ServiceEndPointWatcher resolver; - await using ((resolver = resolverFactory.CreateWatcher("http://basket")).ConfigureAwait(false)) + ServiceEndpointWatcher watcher; + await using ((watcher = watcherFactory.CreateWatcher("http://basket")).ConfigureAwait(false)) { - Assert.NotNull(resolver); - var initialEndPointsTask = resolver.GetEndPointsAsync(CancellationToken.None).ConfigureAwait(false); + Assert.NotNull(watcher); + var initialEndpointsTask = watcher.GetEndpointsAsync(CancellationToken.None).ConfigureAwait(false); sem.Release(1); - var initialEndPoints = await initialEndPointsTask; - Assert.NotNull(initialEndPoints); - Assert.Single(initialEndPoints.EndPoints); + var initialEndpoints = await initialEndpointsTask; + Assert.NotNull(initialEndpoints); + Assert.Single(initialEndpoints.Endpoints); // Tell the resolver to throw on the next resolve call and then trigger a reload. throwOnNextResolve[0] = true; @@ -254,21 +254,21 @@ public async Task ResolveServiceEndPoint_ThrowOnReload() var exception = await Assert.ThrowsAsync(async () => { - var resolveTask = resolver.GetEndPointsAsync(CancellationToken.None); + var resolveTask = watcher.GetEndpointsAsync(CancellationToken.None); sem.Release(1); await resolveTask.ConfigureAwait(false); }).ConfigureAwait(false); Assert.Equal("throwing", exception.Message); - var channel = Channel.CreateUnbounded(); - resolver.OnEndPointsUpdated = result => channel.Writer.TryWrite(result); + var channel = Channel.CreateUnbounded(); + watcher.OnEndpointsUpdated = result => channel.Writer.TryWrite(result); do { cts[0].Cancel(); sem.Release(1); - var resolveTask = resolver.GetEndPointsAsync(CancellationToken.None); + var resolveTask = watcher.GetEndpointsAsync(CancellationToken.None); await resolveTask.ConfigureAwait(false); var next = await channel.Reader.ReadAsync(CancellationToken.None).ConfigureAwait(false); if (next.ResolvedSuccessfully) @@ -277,11 +277,11 @@ public async Task ResolveServiceEndPoint_ThrowOnReload() } } while (true); - var task = resolver.GetEndPointsAsync(CancellationToken.None); + var task = watcher.GetEndpointsAsync(CancellationToken.None); sem.Release(1); var result = await task.ConfigureAwait(false); - Assert.NotSame(initialEndPoints, result); - var sep = Assert.Single(result.EndPoints); + Assert.NotSame(initialEndpoints, result); + var sep = Assert.Single(result.Endpoints); var ip = Assert.IsType(sep.EndPoint); Assert.Equal(IPAddress.Parse("127.1.1.1"), ip.Address); Assert.Equal(8080, ip.Port);