Skip to content

Commit ce25be6

Browse files
github-actions[bot]DamianEdwardsBrennanConroy
authored
[release/9.4] Add non-localhost URLs (#10396)
* Add non-localhost URLs * Fix build * *.localhost * test * cleanup * Apply suggestions from code review --------- Co-authored-by: Damian Edwards <[email protected]> Co-authored-by: Brennan <[email protected]>
1 parent 8abb3d1 commit ce25be6

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
@@ -1581,6 +1581,7 @@ private static (string, EndpointBindingMode) NormalizeTargetHost(string targetHo
15811581
{
15821582
null or "" => ("localhost", EndpointBindingMode.SingleAddress), // Default is localhost
15831583
var s when string.Equals(s, "localhost", StringComparison.OrdinalIgnoreCase) => ("localhost", EndpointBindingMode.SingleAddress), // Explicitly set to localhost
1584+
var s when s.Length > 10 && s.EndsWith(".localhost", StringComparison.OrdinalIgnoreCase) => ("localhost", EndpointBindingMode.SingleAddress), // Explicitly set to localhost when using .localhost subdomain
15841585
var s when IPAddress.TryParse(s, out var ipAddress) => ipAddress switch // The host is an IP address
15851586
{
15861587
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
@@ -617,6 +617,72 @@ public void WithEndpoint_WithAllArguments_ForwardsAllArguments()
617617
Assert.Equal(System.Net.Sockets.ProtocolType.Tcp, endpoint.Protocol);
618618
}
619619

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

0 commit comments

Comments
 (0)