diff --git a/src/Libraries/Microsoft.Extensions.ServiceDiscovery.Abstractions/ServiceEndpoint.cs b/src/Libraries/Microsoft.Extensions.ServiceDiscovery.Abstractions/ServiceEndpoint.cs
index 33e0eff4d69..e5a10374adc 100644
--- a/src/Libraries/Microsoft.Extensions.ServiceDiscovery.Abstractions/ServiceEndpoint.cs
+++ b/src/Libraries/Microsoft.Extensions.ServiceDiscovery.Abstractions/ServiceEndpoint.cs
@@ -1,6 +1,7 @@
// 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 System.Net;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.ServiceDiscovery.Internal;
@@ -34,4 +35,52 @@ public static ServiceEndpoint Create(EndPoint endPoint, IFeatureCollection? feat
return new ServiceEndpointImpl(endPoint, features);
}
+
+ ///
+ /// Tries to convert a specified string representation to its equivalent,
+ /// and returns a value that indicates whether the conversion succeeded.
+ ///
+ /// A string that consists of an IP address or hostname, optionally followed by a colon and port number, or a URI.
+ /// When this method returns, contains the equivalent if the conversion succeeded; otherwise,
+ /// . This parameter is passed uninitialized; any value originally supplied will be overwritten.
+ /// if the string was successfully parsed into a ; otherwise, .
+ public static bool TryParse([NotNullWhen(true)] string? value,
+ [NotNullWhen(true)] out ServiceEndpoint? serviceEndpoint)
+ {
+ EndPoint? endPoint = TryParseEndPoint(value);
+
+ if (endPoint != null)
+ {
+ serviceEndpoint = Create(endPoint);
+ return true;
+ }
+ else
+ {
+ serviceEndpoint = null;
+ return false;
+ }
+ }
+
+ private static EndPoint? TryParseEndPoint(string? value)
+ {
+ if (!string.IsNullOrWhiteSpace(value))
+ {
+#pragma warning disable CS8602
+ if (value.IndexOf("://", StringComparison.Ordinal) < 0 && Uri.TryCreate($"fakescheme://{value}", default, out var uri))
+#pragma warning restore CS8602
+ {
+ var port = uri.Port > 0 ? uri.Port : 0;
+ return IPAddress.TryParse(uri.Host, out var ip)
+ ? new IPEndPoint(ip, port)
+ : new DnsEndPoint(uri.Host, port);
+ }
+
+ if (Uri.TryCreate(value, default, out uri))
+ {
+ return new UriEndPoint(uri);
+ }
+ }
+
+ return null;
+ }
}
diff --git a/src/Libraries/Microsoft.Extensions.ServiceDiscovery/UriEndPoint.cs b/src/Libraries/Microsoft.Extensions.ServiceDiscovery.Abstractions/UriEndPoint.cs
similarity index 63%
rename from src/Libraries/Microsoft.Extensions.ServiceDiscovery/UriEndPoint.cs
rename to src/Libraries/Microsoft.Extensions.ServiceDiscovery.Abstractions/UriEndPoint.cs
index 6b5b07d199e..d7bb1ab040e 100644
--- a/src/Libraries/Microsoft.Extensions.ServiceDiscovery/UriEndPoint.cs
+++ b/src/Libraries/Microsoft.Extensions.ServiceDiscovery.Abstractions/UriEndPoint.cs
@@ -8,13 +8,22 @@ namespace Microsoft.Extensions.ServiceDiscovery;
///
/// An endpoint represented by a .
///
-/// The .
-internal sealed class UriEndPoint(Uri uri) : EndPoint
+public class UriEndPoint : EndPoint
{
+ ///
+ /// Creates a new .
+ ///
+ /// The .
+ public UriEndPoint(Uri uri)
+ {
+ ArgumentNullException.ThrowIfNull(uri);
+ Uri = uri;
+ }
+
///
/// Gets the associated with this endpoint.
///
- public Uri Uri => uri;
+ public Uri Uri { get; }
///
public override bool Equals(object? obj)
@@ -26,5 +35,5 @@ public override bool Equals(object? obj)
public override int GetHashCode() => Uri.GetHashCode();
///
- public override string? ToString() => uri.ToString();
+ public override string? ToString() => Uri.ToString();
}
diff --git a/src/Libraries/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndpointProvider.cs b/src/Libraries/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndpointProvider.cs
index e8c84b69ec8..72d6e7b6b81 100644
--- a/src/Libraries/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndpointProvider.cs
+++ b/src/Libraries/Microsoft.Extensions.ServiceDiscovery/Configuration/ConfigurationServiceEndpointProvider.cs
@@ -1,8 +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.CodeAnalysis;
-using System.Net;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@@ -41,7 +39,8 @@ public ConfigurationServiceEndpointProvider(
_serviceName = query.ServiceName;
_endpointName = query.EndpointName;
_includeAllSchemes = serviceDiscoveryOptions.Value.AllowAllSchemes && query.IncludedSchemes.Count == 0;
- _schemes = ServiceDiscoveryOptions.ApplyAllowedSchemes(query.IncludedSchemes, serviceDiscoveryOptions.Value.AllowedSchemes, serviceDiscoveryOptions.Value.AllowAllSchemes);
+ var allowedSchemes = serviceDiscoveryOptions.Value.ApplyAllowedSchemes(query.IncludedSchemes);
+ _schemes = allowedSchemes as string[] ?? allowedSchemes.ToArray();
_configuration = configuration;
_logger = logger;
_options = options;
@@ -190,52 +189,18 @@ public ValueTask PopulateAsync(IServiceEndpointBuilder endpoints, CancellationTo
private void AddEndpoint(List endpoints, IConfigurationSection section, string endpointName)
{
- var value = section.Value;
- if (string.IsNullOrWhiteSpace(value) || !TryParseEndPoint(value, out var endPoint))
+ if (!ServiceEndpoint.TryParse(section.Value, out var serviceEndpoint))
{
throw new KeyNotFoundException($"The endpoint configuration section for service '{_serviceName}' endpoint '{endpointName}' has an invalid value with key '{section.Key}'.");
}
- endpoints.Add(CreateEndpoint(endPoint));
- }
-
- private static bool TryParseEndPoint(string value, [NotNullWhen(true)] out EndPoint? endPoint)
- {
- if (value.IndexOf("://") < 0 && Uri.TryCreate($"fakescheme://{value}", default, out var uri))
- {
- var port = uri.Port > 0 ? uri.Port : 0;
- if (IPAddress.TryParse(uri.Host, out var ip))
- {
- endPoint = new IPEndPoint(ip, port);
- }
- else
- {
- endPoint = new DnsEndPoint(uri.Host, port);
- }
- }
- else if (Uri.TryCreate(value, default, out uri))
- {
- endPoint = new UriEndPoint(uri);
- }
- else
- {
- endPoint = null;
- return false;
- }
-
- return true;
- }
-
- private ServiceEndpoint CreateEndpoint(EndPoint endPoint)
- {
- var serviceEndpoint = ServiceEndpoint.Create(endPoint);
serviceEndpoint.Features.Set(this);
if (_options.Value.ShouldApplyHostNameMetadata(serviceEndpoint))
{
serviceEndpoint.Features.Set(this);
}
- return serviceEndpoint;
+ endpoints.Add(serviceEndpoint);
}
public override string ToString() => "Configuration";
diff --git a/src/Libraries/Microsoft.Extensions.ServiceDiscovery/ServiceDiscoveryOptions.cs b/src/Libraries/Microsoft.Extensions.ServiceDiscovery/ServiceDiscoveryOptions.cs
index edc652507d9..a6fd7123aa7 100644
--- a/src/Libraries/Microsoft.Extensions.ServiceDiscovery/ServiceDiscoveryOptions.cs
+++ b/src/Libraries/Microsoft.Extensions.ServiceDiscovery/ServiceDiscoveryOptions.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Collections.ObjectModel;
using Microsoft.Extensions.Primitives;
namespace Microsoft.Extensions.ServiceDiscovery;
@@ -30,24 +31,30 @@ public sealed class ServiceDiscoveryOptions
///
public IList AllowedSchemes { get; set; } = new List();
- internal static string[] ApplyAllowedSchemes(IReadOnlyList schemes, IList allowedSchemes, bool allowAllSchemes)
+ ///
+ /// Filters the specified URI schemes to include only those that are applicable, based on the current settings.
+ ///
+ /// The URI schemes to be evaluated against the allowed schemes.
+ ///
+ /// The URI schemes that are applicable. If no schemes are requested, all allowed schemes are returned.
+ /// If all schemes are allowed, only the requested schemes are returned.
+ /// Otherwise, the intersection of requested and allowed schemes is returned.
+ ///
+ public IReadOnlyList ApplyAllowedSchemes(IReadOnlyList requestedSchemes)
{
- if (schemes.Count > 0)
+ ArgumentNullException.ThrowIfNull(requestedSchemes);
+
+ if (requestedSchemes.Count > 0)
{
- if (allowAllSchemes)
+ if (AllowAllSchemes)
{
- if (schemes is string[] array && array.Length > 0)
- {
- return array;
- }
-
- return schemes.ToArray();
+ return requestedSchemes;
}
List result = [];
- foreach (var s in schemes)
+ foreach (var s in requestedSchemes)
{
- foreach (var allowed in allowedSchemes)
+ foreach (var allowed in AllowedSchemes)
{
if (string.Equals(s, allowed, StringComparison.OrdinalIgnoreCase))
{
@@ -57,10 +64,10 @@ internal static string[] ApplyAllowedSchemes(IReadOnlyList schemes, ILis
}
}
- return result.ToArray();
+ return result.AsReadOnly();
}
// If no schemes were specified, but a set of allowed schemes were specified, allow those.
- return allowedSchemes.ToArray();
+ return new ReadOnlyCollection(AllowedSchemes);
}
}