Skip to content

Commit 04fc33c

Browse files
Add non-localhost URLs (#10232)
* Add non-localhost URLs * Fix build * *.localhost * test * cleanup * Apply suggestions from code review --------- Co-authored-by: Brennan <[email protected]>
1 parent f968819 commit 04fc33c

File tree

3 files changed

+83
-1
lines changed

3 files changed

+83
-1
lines changed

src/Aspire.Hosting/Dcp/DcpExecutor.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1587,6 +1587,7 @@ private static (string, EndpointBindingMode) NormalizeTargetHost(string targetHo
15871587
{
15881588
null or "" => ("localhost", EndpointBindingMode.SingleAddress), // Default is localhost
15891589
var s when string.Equals(s, "localhost", StringComparison.OrdinalIgnoreCase) => ("localhost", EndpointBindingMode.SingleAddress), // Explicitly set to localhost
1590+
var s when s.Length > 10 && s.EndsWith(".localhost", StringComparison.OrdinalIgnoreCase) => ("localhost", EndpointBindingMode.SingleAddress), // Explicitly set to localhost when using .localhost subdomain
15901591
var s when IPAddress.TryParse(s, out var ipAddress) => ipAddress switch // The host is an IP address
15911592
{
15921593
var ip when IPAddress.Any.Equals(ip) => ("localhost", EndpointBindingMode.IPv4AnyAddresses), // 0.0.0.0 (IPv4 all addresses)

src/Aspire.Hosting/Orchestrator/ApplicationOrchestrator.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,23 @@ private async Task ProcessResourceUrlCallbacks(IResource resource, CancellationT
212212
Debug.Assert(endpoint.AllocatedEndpoint is not null, "Endpoint should be allocated at this point as we're calling this from ResourceEndpointsAllocatedEvent handler.");
213213
if (endpoint.AllocatedEndpoint is { } allocatedEndpoint)
214214
{
215-
var url = new ResourceUrlAnnotation { Url = allocatedEndpoint.UriString, Endpoint = new EndpointReference(resourceWithEndpoints, endpoint) };
215+
var endpointReference = new EndpointReference(resourceWithEndpoints, endpoint);
216+
var url = new ResourceUrlAnnotation { Url = allocatedEndpoint.UriString, Endpoint = endpointReference };
216217
urls.Add(url);
218+
if (allocatedEndpoint.BindingMode != EndpointBindingMode.SingleAddress && (endpoint.TargetHost is not "localhost" or "127.0.0.1"))
219+
{
220+
// Endpoint is listening on multiple addresses so add another URL based on the declared target hostname
221+
// For endpoints targeting all external addresses (IPv4 0.0.0.0 or IPv6 ::) use the machine name
222+
var address = endpoint.TargetHost is "0.0.0.0" or "::" ? Environment.MachineName : endpoint.TargetHost;
223+
url = new ResourceUrlAnnotation { Url = $"{allocatedEndpoint.UriScheme}://{address}:{allocatedEndpoint.Port}", Endpoint = endpointReference };
224+
urls.Add(url);
225+
}
226+
else if (endpoint.TargetHost.Length > 10 && endpoint.TargetHost.EndsWith(".localhost", StringComparison.OrdinalIgnoreCase))
227+
{
228+
// Add the originally declared *.localhost URL
229+
url = new ResourceUrlAnnotation { Url = $"{allocatedEndpoint.UriScheme}://{endpoint.TargetHost}:{allocatedEndpoint.Port}", Endpoint = endpointReference };
230+
urls.Add(url);
231+
}
217232
}
218233
}
219234
}

tests/Aspire.Hosting.Tests/WithEndpointTests.cs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,72 @@ public void WithEndpoint_WithAllArguments_ForwardsAllArguments()
616616
Assert.Equal(System.Net.Sockets.ProtocolType.Tcp, endpoint.Protocol);
617617
}
618618

619+
[Fact]
620+
public async Task LocalhostTopLevelDomainSetsAnnotationValues()
621+
{
622+
using var builder = TestDistributedApplicationBuilder.Create();
623+
624+
var tcs = new TaskCompletionSource();
625+
var projectA = builder.AddProject<ProjectA>("projecta")
626+
.WithHttpsEndpoint()
627+
.WithEndpoint("https", e => e.TargetHost = "example.localhost", createIfNotExists: false)
628+
.OnBeforeResourceStarted((_, _, _) =>
629+
{
630+
tcs.SetResult();
631+
return Task.CompletedTask;
632+
});
633+
634+
var app = await builder.BuildAsync();
635+
await app.StartAsync();
636+
await tcs.Task;
637+
638+
var urls = projectA.Resource.Annotations.OfType<ResourceUrlAnnotation>();
639+
Assert.Collection(urls,
640+
url => Assert.StartsWith("https://localhost:", url.Url),
641+
url => Assert.StartsWith("https://example.localhost:", url.Url));
642+
643+
EndpointAnnotation endpoint = Assert.Single(projectA.Resource.Annotations.OfType<EndpointAnnotation>());
644+
Assert.NotNull(endpoint.AllocatedEndpoint);
645+
Assert.Equal(EndpointBindingMode.SingleAddress, endpoint.AllocatedEndpoint.BindingMode);
646+
Assert.Equal("localhost", endpoint.AllocatedEndpoint.Address);
647+
648+
await app.StopAsync();
649+
}
650+
651+
[Theory]
652+
[InlineData("0.0.0.0", EndpointBindingMode.IPv4AnyAddresses)]
653+
//[InlineData("::", EndpointBindingMode.IPv6AnyAddresses)] // Need to figure out a good way to check that Ipv6 binding is supported
654+
public async Task TopLevelDomainSetsAnnotationValues(string host, EndpointBindingMode endpointBindingMode)
655+
{
656+
using var builder = TestDistributedApplicationBuilder.Create();
657+
658+
var tcs = new TaskCompletionSource();
659+
var projectA = builder.AddProject<ProjectA>("projecta")
660+
.WithHttpsEndpoint()
661+
.WithEndpoint("https", e => e.TargetHost = host, createIfNotExists: false)
662+
.OnBeforeResourceStarted((_, _, _) =>
663+
{
664+
tcs.SetResult();
665+
return Task.CompletedTask;
666+
});
667+
668+
var app = await builder.BuildAsync();
669+
await app.StartAsync();
670+
await tcs.Task;
671+
672+
var urls = projectA.Resource.Annotations.OfType<ResourceUrlAnnotation>();
673+
Assert.Collection(urls,
674+
url => Assert.StartsWith("https://localhost:", url.Url),
675+
url => Assert.StartsWith($"https://{Environment.MachineName}:", url.Url));
676+
677+
EndpointAnnotation endpoint = Assert.Single(projectA.Resource.Annotations.OfType<EndpointAnnotation>());
678+
Assert.NotNull(endpoint.AllocatedEndpoint);
679+
Assert.Equal(endpointBindingMode, endpoint.AllocatedEndpoint.BindingMode);
680+
Assert.Equal("localhost", endpoint.AllocatedEndpoint.Address);
681+
682+
await app.StopAsync();
683+
}
684+
619685
private sealed class TestProject : IProjectMetadata
620686
{
621687
public string ProjectPath => "projectpath";

0 commit comments

Comments
 (0)