Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion src/Aspire.Hosting/Dashboard/DashboardServiceHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ void ConfigureKestrel(KestrelServerOptions kestrelOptions)
kestrelOptions.Listen(IPAddress.Loopback, port: 0, ConfigureListen);
_logger.LogDebug("Resource service endpoint not configured. Listening on {Scheme}://127.0.0.1:<random>.", scheme);
}
else if (uri.IsLoopback)
else if (IsLocalResourceServiceEndpoint(uri))
{
// Listen on the requested localhost port.
kestrelOptions.ListenLocalhost(uri.Port, ConfigureListen);
Expand Down Expand Up @@ -178,6 +178,23 @@ internal static string ResolveScheme(Uri? configuredUri, bool allowUnsecuredTran
return allowUnsecuredTransport ? "http" : "https";
}

/// <summary>
/// Determines whether the resource service endpoint is scoped to the local machine.
/// </summary>
internal static bool IsLocalResourceServiceEndpoint(Uri uri)
{
if (uri.IsLoopback)
{
return true;
}

var host = uri.Host.EndsWith(".", StringComparison.Ordinal)
? uri.Host[..^1]
: uri.Host;

return EndpointHostHelpers.IsLocalhostOrLocalhostTld(host);
}

/// <summary>
/// Gets the URI upon which the resource service is listening.
/// </summary>
Expand Down
48 changes: 48 additions & 0 deletions tests/Aspire.Cli.EndToEnd.Tests/SmokeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,54 @@ await auto.WaitUntilAsync(s =>
await pendingRun;
}

[CaptureWorkspaceOnFailure]
[Fact]
public async Task CreateAndRunPolyglotAppHostWithDevLocalhostUrls()
{
var repoRoot = CliE2ETestHelpers.GetRepoRoot();
var strategy = CliInstallStrategy.Detect(output.WriteLine);

var workspace = TemporaryWorkspace.Create(output);

using var terminal = CliE2ETestHelpers.CreateDockerTestTerminal(repoRoot, strategy, output, mountDockerSocket: true, workspace: workspace);

var pendingRun = terminal.RunAsync(TestContext.Current.CancellationToken);

var counter = new SequenceCounter();
var auto = new Hex1bTerminalAutomator(terminal, defaultTimeout: TimeSpan.FromSeconds(500));

await auto.PrepareDockerEnvironmentAsync(counter, workspace);
await auto.InstallAspireCliAsync(strategy, counter);

const string projectName = "PolyglotDevLocalhost";
await auto.AspireNewAsync(projectName, counter, template: AspireTemplate.ExpressReact, useDevLocalhost: true);

await auto.RunCommandAsync($"cd {projectName}", counter);
await auto.RunCommandAsync("grep -F 'ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL' aspire.config.json && grep -F 'polyglotdevlocalhost.dev.localhost' aspire.config.json", counter);

await auto.TypeAsync("aspire run");
await auto.EnterAsync();

await auto.WaitUntilAsync(s =>
{
if (s.ContainsText("Capability Error") ||
s.ContainsText("ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL must contain a local loopback address"))
{
throw new InvalidOperationException("Polyglot AppHost failed to start with a *.dev.localhost resource service endpoint.");
}

return s.ContainsText("Press CTRL+C to stop the AppHost and exit.");
}, timeout: TimeSpan.FromMinutes(3), description: "Press CTRL+C message for polyglot AppHost with *.dev.localhost URLs");

await auto.Ctrl().KeyAsync(Hex1bKey.C);
await auto.WaitForSuccessPromptAsync(counter);

await auto.TypeAsync("exit");
await auto.EnterAsync();

await pendingRun;
}

[CaptureWorkspaceOnFailure]
[Fact]
public async Task LatestCliCanStartStableChannelAppHost()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,21 @@ public void ResolveScheme_ReturnsExpectedScheme(string? uriString, bool allowUns

Assert.Equal(expectedScheme, scheme);
}

[Theory]
[InlineData("https://localhost:5001", true)]
[InlineData("https://localhost.:5001", true)]
[InlineData("https://127.0.0.1:5001", true)]
[InlineData("https://[::1]:5001", true)]
[InlineData("https://myapp.dev.localhost:5001", true)]
[InlineData("https://myapp.dev.localhost.:5001", true)]
[InlineData("https://example.com:5001", false)]
[InlineData("https://localhost.example.com:5001", false)]
[InlineData("https://example-localhost:5001", false)]
public void IsLocalResourceServiceEndpoint_ReturnsExpectedResult(string uriString, bool expectedResult)
{
var result = DashboardServiceHost.IsLocalResourceServiceEndpoint(new Uri(uriString));

Assert.Equal(expectedResult, result);
}
}
Loading