diff --git a/.github/workflows/TestHostName.yml b/.github/workflows/TestHostName.yml
new file mode 100644
index 0000000000..e463f8ea1a
--- /dev/null
+++ b/.github/workflows/TestHostName.yml
@@ -0,0 +1,50 @@
+name: TestHostName
+
+on:
+ workflow_dispatch:
+ push:
+ branches:
+ - main
+ - '[0-9]+.x'
+ pull_request:
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+permissions:
+ contents: read
+ pull-requests: write
+
+env:
+ DOTNET_CLI_TELEMETRY_OPTOUT: 1
+ DOTNET_NOLOGO: true
+
+jobs:
+ testHostName:
+ name: Test hostname on ${{ matrix.os }}
+ timeout-minutes: 15
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, windows-latest, macos-latest]
+ include:
+ - os: ubuntu-latest
+ - os: windows-latest
+ - os: macos-latest
+ runs-on: ${{ matrix.os }}
+ continue-on-error: true
+
+ steps:
+ - name: resolve1
+ if: always()
+ shell: pwsh
+ run: |
+ $hostName = [System.Net.Dns]::GetHostName()
+ [System.Net.Dns]::GetHostEntry($hostName)
+ - name: resolve2
+ if: always()
+ shell: pwsh
+ run: |
+ $hostName = [System.Net.Dns]::GetHostName()
+ [System.Net.Dns]::GetHostEntry($hostName + ".local")
diff --git a/src/Common/src/Common/Net/DomainNameResolver.cs b/src/Common/src/Common/Net/DomainNameResolver.cs
index 0b1da12ddd..879e3548ec 100644
--- a/src/Common/src/Common/Net/DomainNameResolver.cs
+++ b/src/Common/src/Common/Net/DomainNameResolver.cs
@@ -42,28 +42,49 @@ private DomainNameResolver()
public string? ResolveHostName(bool throwOnError = false)
{
+ string? resultingHostName = null;
+ string? resultingHostEntryHostName = null;
+ bool? workaroundApplied = null;
+
try
{
string hostName = Dns.GetHostName();
+ resultingHostName = hostName;
if (string.IsNullOrEmpty(hostName))
{
// Workaround for failure when running on macOS.
// See https://github.com/actions/runner-images/issues/1335 and https://github.com/dotnet/runtime/issues/36849.
+
hostName = "localhost";
+ workaroundApplied = true;
}
IPHostEntry hostEntry = Dns.GetHostEntry(hostName);
- return hostEntry.HostName;
+ resultingHostEntryHostName = hostEntry.HostName;
+
+ if (string.IsNullOrEmpty(resultingHostEntryHostName))
+ {
+ throw new InvalidOperationException($"IPHostEntry.HostName is {GetTextFor(resultingHostEntryHostName)}.");
+ }
+
+ return resultingHostEntryHostName;
}
- catch (Exception)
+ catch (Exception exception)
{
if (throwOnError)
{
- throw;
+ throw new InvalidOperationException(
+ $"Failed to resolve hostname. First={GetTextFor(resultingHostName)}, Second={GetTextFor(resultingHostEntryHostName)}, WorkaroundApplied={workaroundApplied}",
+ exception);
}
return null;
}
}
+
+ private static string GetTextFor(string? value)
+ {
+ return value == null ? "null" : "empty";
+ }
}
diff --git a/src/Discovery/src/Consul/PostConfigureConsulDiscoveryOptions.cs b/src/Discovery/src/Consul/PostConfigureConsulDiscoveryOptions.cs
index 6ad1371335..aa6f48820c 100644
--- a/src/Discovery/src/Consul/PostConfigureConsulDiscoveryOptions.cs
+++ b/src/Discovery/src/Consul/PostConfigureConsulDiscoveryOptions.cs
@@ -43,7 +43,7 @@ public void PostConfigure(string? name, ConsulDiscoveryOptions options)
options.ServiceName = GetServiceName(options);
HostInfo? hostInfo = options.UseNetworkInterfaces ? _inetUtils.FindFirstNonLoopbackHostInfo() : null;
- options.HostName ??= hostInfo != null ? hostInfo.Hostname : _domainNameResolver.ResolveHostName();
+ options.HostName ??= hostInfo != null ? hostInfo.Hostname : _domainNameResolver.ResolveHostName(true);
if (string.IsNullOrWhiteSpace(options.IPAddress))
{
diff --git a/src/Discovery/src/Consul/Registry/ConsulRegistration.cs b/src/Discovery/src/Consul/Registry/ConsulRegistration.cs
index c8861fd4d4..81bd573afc 100644
--- a/src/Discovery/src/Consul/Registry/ConsulRegistration.cs
+++ b/src/Discovery/src/Consul/Registry/ConsulRegistration.cs
@@ -35,7 +35,7 @@ internal sealed class ConsulRegistration : IServiceInstance
public bool IsSecure => _optionsMonitor.CurrentValue.EffectiveScheme == "https";
///
- public Uri Uri => new($"{_optionsMonitor.CurrentValue.EffectiveScheme}://{Host}:{Port}");
+ public Uri Uri => FormatUri();
///
public Uri? NonSecureUri => IsSecure ? null : Uri;
@@ -73,6 +73,20 @@ internal ConsulRegistration(AgentServiceRegistration innerRegistration, IOptions
Metadata = innerRegistration.Meta.AsReadOnly();
}
+ private Uri FormatUri()
+ {
+ string scheme = _optionsMonitor.CurrentValue.EffectiveScheme;
+
+ try
+ {
+ return new Uri($"{scheme}://{Host}:{Port}");
+ }
+ catch (UriFormatException exception)
+ {
+ throw new UriFormatException($"Failed to build URI from components. Scheme={scheme}, Host={Host},Port={Port}.", exception);
+ }
+ }
+
///
/// Creates a registration for the currently running app, to be submitted to the Consul server.
///