diff --git a/src/Discovery/src/Consul/Configuration/ConsulDiscoveryOptions.cs b/src/Discovery/src/Consul/Configuration/ConsulDiscoveryOptions.cs
index b0758008e8..fd9b8b77d9 100644
--- a/src/Discovery/src/Consul/Configuration/ConsulDiscoveryOptions.cs
+++ b/src/Discovery/src/Consul/Configuration/ConsulDiscoveryOptions.cs
@@ -175,6 +175,9 @@ public sealed class ConsulDiscoveryOptions
///
/// Gets or sets a value indicating whether to register with the port number ASP.NET Core is listening on. Default value: true.
+ ///
+ /// This property is ignored when or is explicitly configured, or when is
+ /// true.
///
public bool UseAspNetCoreUrls { get; set; } = true;
}
diff --git a/src/Discovery/src/Consul/ConfigurationSchema.json b/src/Discovery/src/Consul/ConfigurationSchema.json
index 3111a531f0..a00e7f69bf 100644
--- a/src/Discovery/src/Consul/ConfigurationSchema.json
+++ b/src/Discovery/src/Consul/ConfigurationSchema.json
@@ -188,7 +188,7 @@
},
"UseAspNetCoreUrls": {
"type": "boolean",
- "description": "Gets or sets a value indicating whether to register with the port number ASP.NET Core is listening on. Default value: true."
+ "description": "Gets or sets a value indicating whether to register with the port number ASP.NET Core is listening on. Default value: true.\n\nThis property is ignored when 'Steeltoe.Discovery.Consul.Configuration.ConsulDiscoveryOptions.Port' or 'Steeltoe.Discovery.Consul.Configuration.ConsulDiscoveryOptions.Scheme' is explicitly configured, or when 'Steeltoe.Discovery.Consul.Configuration.ConsulDiscoveryOptions.UseNetworkInterfaces' is true."
},
"UseNetworkInterfaces": {
"type": "boolean",
diff --git a/src/Discovery/src/Consul/PostConfigureConsulDiscoveryOptions.cs b/src/Discovery/src/Consul/PostConfigureConsulDiscoveryOptions.cs
index 6ad1371335..91dcd274cc 100644
--- a/src/Discovery/src/Consul/PostConfigureConsulDiscoveryOptions.cs
+++ b/src/Discovery/src/Consul/PostConfigureConsulDiscoveryOptions.cs
@@ -62,7 +62,7 @@ public void PostConfigure(string? name, ConsulDiscoveryOptions options)
options.HostName = options.IPAddress;
}
- if (options.Port == 0)
+ if (options is { UseAspNetCoreUrls: true, Port: 0, Scheme: null, UseNetworkInterfaces: false })
{
ICollection addresses = _configuration.GetListenAddresses();
SetSchemeWithPortFromListenAddresses(options, addresses);
@@ -79,38 +79,33 @@ private string GetServiceName(ConsulDiscoveryOptions options)
private void SetSchemeWithPortFromListenAddresses(ConsulDiscoveryOptions options, IEnumerable listenOnAddresses)
{
- // Try to pull some values out of server configuration to override defaults, but only if not using NetUtils.
- // If NetUtils are configured, the user probably wants to define their own behavior.
- if (options is { UseAspNetCoreUrls: true, Port: 0, Scheme: null })
- {
- int? listenHttpPort = null;
- int? listenHttpsPort = null;
+ int? listenHttpPort = null;
+ int? listenHttpsPort = null;
- foreach (string address in listenOnAddresses)
- {
- BindingAddress bindingAddress = BindingAddress.Parse(address);
-
- if (bindingAddress is { Scheme: "http", Port: > 0 } && listenHttpPort == null)
- {
- listenHttpPort = bindingAddress.Port;
- }
- else if (bindingAddress is { Scheme: "https", Port: > 0 } && listenHttpsPort == null)
- {
- listenHttpsPort = bindingAddress.Port;
- }
- }
+ foreach (string address in listenOnAddresses)
+ {
+ BindingAddress bindingAddress = BindingAddress.Parse(address);
- if (listenHttpsPort != null)
+ if (bindingAddress is { Scheme: "http", Port: > 0 } && listenHttpPort == null)
{
- options.Port = listenHttpsPort.Value;
- options.Scheme = "https";
+ listenHttpPort = bindingAddress.Port;
}
- else if (listenHttpPort != null)
+ else if (bindingAddress is { Scheme: "https", Port: > 0 } && listenHttpsPort == null)
{
- options.Port = listenHttpPort.Value;
- options.Scheme = "http";
+ listenHttpsPort = bindingAddress.Port;
}
}
+
+ if (listenHttpsPort != null)
+ {
+ options.Port = listenHttpsPort.Value;
+ options.Scheme = "https";
+ }
+ else if (listenHttpPort != null)
+ {
+ options.Port = listenHttpPort.Value;
+ options.Scheme = "http";
+ }
}
private string GetInstanceId(ConsulDiscoveryOptions options)
diff --git a/src/Discovery/src/Eureka/Configuration/EurekaInstanceOptions.cs b/src/Discovery/src/Eureka/Configuration/EurekaInstanceOptions.cs
index 08e0f3f51e..24b391e3da 100644
--- a/src/Discovery/src/Eureka/Configuration/EurekaInstanceOptions.cs
+++ b/src/Discovery/src/Eureka/Configuration/EurekaInstanceOptions.cs
@@ -20,7 +20,11 @@ public sealed partial class EurekaInstanceOptions
internal const string DefaultStatusPageUrlPath = "/info";
internal const string DefaultHealthCheckUrlPath = "/health";
- private bool UseAspNetCoreUrls => !Platform.IsCloudFoundry || IsContainerToContainerMethod() || IsForceHostNameMethod();
+ private bool IsAnyPortConfigured =>
+ this is { IsNonSecurePortEnabled: true, NonSecurePort: not null } or { IsSecurePortEnabled: true, SecurePort: not null };
+
+ private bool IsSuitableRegistrationMethod => !Platform.IsCloudFoundry || IsContainerToContainerMethod() || IsForceHostNameMethod();
+ internal bool ShouldSetPortsFromListenAddresses => UseAspNetCoreUrls && IsSuitableRegistrationMethod && !IsAnyPortConfigured;
internal TimeSpan LeaseRenewalInterval => TimeSpan.FromSeconds(LeaseRenewalIntervalInSeconds);
internal TimeSpan LeaseExpirationDuration => TimeSpan.FromSeconds(LeaseExpirationDurationInSeconds);
@@ -205,6 +209,13 @@ public sealed partial class EurekaInstanceOptions
Name = DataCenterName.MyOwn
};
+ ///
+ /// Gets or sets a value indicating whether to register with the port number(s) ASP.NET Core is listening on. Default value: true.
+ ///
+ /// This property is ignored when or is explicitly configured.
+ ///
+ public bool UseAspNetCoreUrls { get; set; } = true;
+
///
/// Gets or sets a value indicating whether is used to determine and
/// . Default value: false.
@@ -231,72 +242,69 @@ internal bool IsForceHostNameMethod()
internal void SetPortsFromListenAddresses(IEnumerable listenOnAddresses, string source, ILogger logger)
{
- if (UseAspNetCoreUrls)
+ int? listenHttpPort = null;
+ int? listenHttpsPort = null;
+
+ foreach (string address in listenOnAddresses)
{
- int? listenHttpPort = null;
- int? listenHttpsPort = null;
+ BindingAddress bindingAddress = BindingAddress.Parse(address);
- foreach (string address in listenOnAddresses)
+ if (bindingAddress is { Scheme: "http", Port: > 0 } && listenHttpPort == null)
{
- BindingAddress bindingAddress = BindingAddress.Parse(address);
-
- if (bindingAddress is { Scheme: "http", Port: > 0 } && listenHttpPort == null)
- {
- listenHttpPort = bindingAddress.Port;
- }
- else if (bindingAddress is { Scheme: "https", Port: > 0 } && listenHttpsPort == null)
- {
- listenHttpsPort = bindingAddress.Port;
- }
+ listenHttpPort = bindingAddress.Port;
+ }
+ else if (bindingAddress is { Scheme: "https", Port: > 0 } && listenHttpsPort == null)
+ {
+ listenHttpsPort = bindingAddress.Port;
}
+ }
- int? nonSecurePort = IsNonSecurePortEnabled ? NonSecurePort : null;
- int? securePort = IsSecurePortEnabled ? SecurePort : null;
+ int? nonSecurePort = IsNonSecurePortEnabled ? NonSecurePort : null;
+ int? securePort = IsSecurePortEnabled ? SecurePort : null;
- if (nonSecurePort != listenHttpPort)
+ if (nonSecurePort != listenHttpPort)
+ {
+ if (listenHttpPort != null)
{
- if (listenHttpPort != null)
+ if (nonSecurePort == null)
{
- if (nonSecurePort == null)
- {
- LogActivatingNonSecurePort(logger, listenHttpPort, source);
- }
- else
- {
- LogChangingNonSecurePort(logger, listenHttpPort, source);
- }
-
- NonSecurePort = listenHttpPort.Value;
- IsNonSecurePortEnabled = true;
+ LogActivatingNonSecurePort(logger, listenHttpPort, source);
}
- else if (nonSecurePort != null)
+ else
{
- LogDeactivatingNonSecurePort(logger, source);
- IsNonSecurePortEnabled = false;
+ LogChangingNonSecurePort(logger, listenHttpPort, source);
}
+
+ NonSecurePort = listenHttpPort.Value;
+ IsNonSecurePortEnabled = true;
+ }
+ else if (nonSecurePort != null)
+ {
+ LogDeactivatingNonSecurePort(logger, source);
+ IsNonSecurePortEnabled = false;
}
+ }
- if (securePort != listenHttpsPort)
+ if (securePort != listenHttpsPort)
+ {
+ if (listenHttpsPort != null)
{
- if (listenHttpsPort != null)
+ if (securePort == null)
{
- if (securePort == null)
- {
- LogActivatingSecurePort(logger, listenHttpsPort, source);
- }
- else
- {
- LogChangingSecurePort(logger, listenHttpsPort, source);
- }
-
- SecurePort = listenHttpsPort.Value;
- IsSecurePortEnabled = true;
+ LogActivatingSecurePort(logger, listenHttpsPort, source);
}
- else if (securePort != null)
+ else
{
- LogDeactivatingSecurePort(logger, source);
- IsSecurePortEnabled = false;
+ LogChangingSecurePort(logger, listenHttpsPort, source);
}
+
+ SecurePort = listenHttpsPort.Value;
+ IsSecurePortEnabled = true;
+ }
+ else if (securePort != null)
+ {
+ LogDeactivatingSecurePort(logger, source);
+ IsSecurePortEnabled = false;
}
}
}
diff --git a/src/Discovery/src/Eureka/ConfigurationSchema.json b/src/Discovery/src/Eureka/ConfigurationSchema.json
index 2e1c600204..95634e3416 100644
--- a/src/Discovery/src/Eureka/ConfigurationSchema.json
+++ b/src/Discovery/src/Eureka/ConfigurationSchema.json
@@ -258,6 +258,10 @@
"type": "string",
"description": "Gets or sets the relative path to the status page for the instance. The status page URL is then constructed out of the 'Steeltoe.Discovery.Eureka.Configuration.EurekaInstanceOptions.HostName' and the type of communication - secure or non-secure, as specified in 'Steeltoe.Discovery.Eureka.Configuration.EurekaInstanceOptions.SecurePort' and 'Steeltoe.Discovery.Eureka.Configuration.EurekaInstanceOptions.NonSecurePort'. It is normally used for informational purposes for other services to find out about the status of the instance. Users can provide a simple HTML page indicating what the current status of the instance is. Default value: /info."
},
+ "UseAspNetCoreUrls": {
+ "type": "boolean",
+ "description": "Gets or sets a value indicating whether to register with the port number(s) ASP.NET Core is listening on. Default value: true.\n\nThis property is ignored when 'Steeltoe.Discovery.Eureka.Configuration.EurekaInstanceOptions.NonSecurePort' or 'Steeltoe.Discovery.Eureka.Configuration.EurekaInstanceOptions.SecurePort' is explicitly configured."
+ },
"UseNetworkInterfaces": {
"type": "boolean",
"description": "Gets or sets a value indicating whether 'System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces' is used to determine 'Steeltoe.Discovery.Eureka.Configuration.EurekaInstanceOptions.IPAddress' and 'Steeltoe.Discovery.Eureka.Configuration.EurekaInstanceOptions.HostName'. Default value: false."
diff --git a/src/Discovery/src/Eureka/DynamicPortAssignmentHostedService.cs b/src/Discovery/src/Eureka/DynamicPortAssignmentHostedService.cs
index e58cefb6bf..da517432b7 100644
--- a/src/Discovery/src/Eureka/DynamicPortAssignmentHostedService.cs
+++ b/src/Discovery/src/Eureka/DynamicPortAssignmentHostedService.cs
@@ -136,7 +136,7 @@ public void PostConfigure(string? name, EurekaInstanceOptions options)
{
ArgumentNullException.ThrowIfNull(options);
- if (_listenState.ListenOnAddresses != null)
+ if (_listenState.ListenOnAddresses != null && options.ShouldSetPortsFromListenAddresses)
{
options.SetPortsFromListenAddresses(_listenState.ListenOnAddresses, "address features", _optionsLogger);
}
diff --git a/src/Discovery/src/Eureka/EurekaServiceCollectionExtensions.cs b/src/Discovery/src/Eureka/EurekaServiceCollectionExtensions.cs
index 3348501e08..38de08bce5 100644
--- a/src/Discovery/src/Eureka/EurekaServiceCollectionExtensions.cs
+++ b/src/Discovery/src/Eureka/EurekaServiceCollectionExtensions.cs
@@ -81,11 +81,11 @@ private static void ConfigureEurekaClientOptions(IServiceCollection services)
private static void ConfigureEurekaInstanceOptions(IServiceCollection services)
{
+ DynamicPortAssignmentHostedService.Wire(services);
+
services.AddOptions().BindConfiguration(EurekaInstanceOptions.ConfigurationPrefix);
services.AddOptions().BindConfiguration(InetOptions.ConfigurationPrefix);
services.AddSingleton, PostConfigureEurekaInstanceOptions>();
-
- DynamicPortAssignmentHostedService.Wire(services);
}
private static void AddEurekaServices(IServiceCollection services)
diff --git a/src/Discovery/src/Eureka/PostConfigureEurekaInstanceOptions.cs b/src/Discovery/src/Eureka/PostConfigureEurekaInstanceOptions.cs
index 3ef1f30b96..b33208d520 100644
--- a/src/Discovery/src/Eureka/PostConfigureEurekaInstanceOptions.cs
+++ b/src/Discovery/src/Eureka/PostConfigureEurekaInstanceOptions.cs
@@ -140,7 +140,7 @@ private void SetPorts(EurekaInstanceOptions options)
options.IsSecurePortEnabled = true;
}
- if (options.NonSecurePort == null && options.SecurePort == null)
+ if (options.ShouldSetPortsFromListenAddresses)
{
var optionsLogger = _serviceProvider.GetRequiredService>();
ICollection addresses = _configuration.GetListenAddresses();
diff --git a/src/Discovery/src/Eureka/PublicAPI.Unshipped.txt b/src/Discovery/src/Eureka/PublicAPI.Unshipped.txt
index 7dc5c58110..e09ccfe57f 100644
--- a/src/Discovery/src/Eureka/PublicAPI.Unshipped.txt
+++ b/src/Discovery/src/Eureka/PublicAPI.Unshipped.txt
@@ -1 +1,3 @@
#nullable enable
+Steeltoe.Discovery.Eureka.Configuration.EurekaInstanceOptions.UseAspNetCoreUrls.get -> bool
+Steeltoe.Discovery.Eureka.Configuration.EurekaInstanceOptions.UseAspNetCoreUrls.set -> void
diff --git a/src/Discovery/test/Eureka.Test/DynamicPortAssignmentTest.cs b/src/Discovery/test/Eureka.Test/DynamicPortAssignmentTest.cs
index 45f9666e17..b3adc4687e 100644
--- a/src/Discovery/test/Eureka.Test/DynamicPortAssignmentTest.cs
+++ b/src/Discovery/test/Eureka.Test/DynamicPortAssignmentTest.cs
@@ -4,9 +4,11 @@
using System.Runtime.InteropServices;
using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Steeltoe.Common.TestResources;
+using Steeltoe.Discovery.Eureka.Configuration;
namespace Steeltoe.Discovery.Eureka.Test;
@@ -38,4 +40,147 @@ public async Task Applies_dynamically_assigned_ports_after_startup()
infoManager.Instance.IsSecurePortEnabled.Should().BeTrue();
infoManager.Instance.SecurePort.Should().BePositive();
}
+
+ [Fact]
+ public async Task Applies_dynamically_assigned_ports_when_kestrel_overrides_urls_config()
+ {
+ var appSettings = new Dictionary
+ {
+ ["Eureka:Client:ShouldFetchRegistry"] = "false",
+ ["Eureka:Client:ShouldRegisterWithEureka"] = "false",
+ ["urls"] = "http://*:5000"
+ };
+
+ WebApplicationBuilder builder = TestWebApplicationBuilderFactory.CreateDefault(false);
+ builder.Configuration.AddInMemoryCollection(appSettings);
+
+ builder.WebHost.ConfigureKestrel(options =>
+ {
+ options.ListenAnyIP(0);
+ });
+
+ builder.Services.AddEurekaDiscoveryClient();
+
+ await using WebApplication app = builder.Build();
+ await app.StartAsync(TestContext.Current.CancellationToken);
+
+ var infoManager = app.Services.GetRequiredService();
+
+ infoManager.Instance.IsNonSecurePortEnabled.Should().BeTrue();
+ infoManager.Instance.NonSecurePort.Should().NotBe(5000);
+ infoManager.Instance.NonSecurePort.Should().BePositive();
+ infoManager.Instance.IsSecurePortEnabled.Should().BeFalse();
+ infoManager.Instance.SecurePort.Should().Be(0);
+ }
+
+ [Fact]
+ public async Task Does_not_override_explicitly_configured_secure_port()
+ {
+ var appSettings = new Dictionary
+ {
+ ["Eureka:Client:ShouldFetchRegistry"] = "false",
+ ["Eureka:Client:ShouldRegisterWithEureka"] = "false",
+ ["Eureka:Instance:SecurePort"] = "443",
+ ["Eureka:Instance:SecurePortEnabled"] = "true"
+ };
+
+ WebApplicationBuilder builder = TestWebApplicationBuilderFactory.CreateDefault(false);
+ builder.WebHost.UseSetting("urls", "http://*:0");
+ builder.Configuration.AddInMemoryCollection(appSettings);
+ builder.Services.AddEurekaDiscoveryClient();
+
+ await using WebApplication app = builder.Build();
+ await app.StartAsync(TestContext.Current.CancellationToken);
+
+ var infoManager = app.Services.GetRequiredService();
+
+ infoManager.Instance.IsSecurePortEnabled.Should().BeTrue();
+ infoManager.Instance.SecurePort.Should().Be(443);
+ infoManager.Instance.IsNonSecurePortEnabled.Should().BeFalse();
+ infoManager.Instance.NonSecurePort.Should().Be(0);
+ }
+
+ [Fact]
+ public async Task Does_not_override_explicitly_configured_non_secure_port()
+ {
+ var appSettings = new Dictionary
+ {
+ ["Eureka:Client:ShouldFetchRegistry"] = "false",
+ ["Eureka:Client:ShouldRegisterWithEureka"] = "false",
+ ["Eureka:Instance:Port"] = "80",
+ ["Eureka:Instance:NonSecurePortEnabled"] = "true"
+ };
+
+ WebApplicationBuilder builder = TestWebApplicationBuilderFactory.CreateDefault(false);
+ builder.WebHost.UseSetting("urls", "http://*:0");
+ builder.Configuration.AddInMemoryCollection(appSettings);
+ builder.Services.AddEurekaDiscoveryClient();
+
+ await using WebApplication app = builder.Build();
+ await app.StartAsync(TestContext.Current.CancellationToken);
+
+ var infoManager = app.Services.GetRequiredService();
+
+ infoManager.Instance.IsNonSecurePortEnabled.Should().BeTrue();
+ infoManager.Instance.NonSecurePort.Should().Be(80);
+ infoManager.Instance.IsSecurePortEnabled.Should().BeFalse();
+ infoManager.Instance.SecurePort.Should().Be(0);
+ }
+
+ [Fact]
+ public async Task Does_not_override_ports_configured_via_code()
+ {
+ var appSettings = new Dictionary
+ {
+ ["Eureka:Client:ShouldFetchRegistry"] = "false",
+ ["Eureka:Client:ShouldRegisterWithEureka"] = "false"
+ };
+
+ WebApplicationBuilder builder = TestWebApplicationBuilderFactory.CreateDefault(false);
+ builder.WebHost.UseSetting("urls", "http://*:0");
+ builder.Configuration.AddInMemoryCollection(appSettings);
+ builder.Services.AddEurekaDiscoveryClient();
+
+ builder.Services.Configure(options =>
+ {
+ options.SecurePort = 8443;
+ options.IsSecurePortEnabled = true;
+ });
+
+ await using WebApplication app = builder.Build();
+ await app.StartAsync(TestContext.Current.CancellationToken);
+
+ var infoManager = app.Services.GetRequiredService();
+
+ infoManager.Instance.IsSecurePortEnabled.Should().BeTrue();
+ infoManager.Instance.SecurePort.Should().Be(8443);
+ infoManager.Instance.IsNonSecurePortEnabled.Should().BeFalse();
+ infoManager.Instance.NonSecurePort.Should().Be(0);
+ }
+
+ [Fact]
+ public async Task Does_not_apply_dynamic_ports_when_UseAspNetCoreUrls_is_false()
+ {
+ var appSettings = new Dictionary
+ {
+ ["Eureka:Client:ShouldFetchRegistry"] = "false",
+ ["Eureka:Client:ShouldRegisterWithEureka"] = "false",
+ ["Eureka:Instance:UseAspNetCoreUrls"] = "false"
+ };
+
+ WebApplicationBuilder builder = TestWebApplicationBuilderFactory.CreateDefault(false);
+ builder.WebHost.UseSetting("urls", "http://*:0");
+ builder.Configuration.AddInMemoryCollection(appSettings);
+ builder.Services.AddEurekaDiscoveryClient();
+
+ await using WebApplication app = builder.Build();
+ await app.StartAsync(TestContext.Current.CancellationToken);
+
+ var infoManager = app.Services.GetRequiredService();
+
+ infoManager.Instance.IsNonSecurePortEnabled.Should().BeFalse();
+ infoManager.Instance.NonSecurePort.Should().Be(0);
+ infoManager.Instance.IsSecurePortEnabled.Should().BeFalse();
+ infoManager.Instance.SecurePort.Should().Be(0);
+ }
}
diff --git a/src/Discovery/test/Eureka.Test/EurekaInstanceOptionsTest.cs b/src/Discovery/test/Eureka.Test/EurekaInstanceOptionsTest.cs
index d5a1a9ee6f..180fe7fbf9 100644
--- a/src/Discovery/test/Eureka.Test/EurekaInstanceOptionsTest.cs
+++ b/src/Discovery/test/Eureka.Test/EurekaInstanceOptionsTest.cs
@@ -43,6 +43,7 @@ public void Constructor_Initializes_Defaults()
instanceOptions.SecureHealthCheckUrl.Should().BeNull();
instanceOptions.AutoScalingGroupName.Should().BeNull();
instanceOptions.DataCenterInfo.Name.Should().Be(DataCenterName.MyOwn);
+ instanceOptions.UseAspNetCoreUrls.Should().BeTrue();
instanceOptions.UseNetworkInterfaces.Should().BeFalse();
}