diff --git a/src/Aspire.Hosting.Yarp/YarpResource.cs b/src/Aspire.Hosting.Yarp/YarpResource.cs index 392b84148f7..09f3fa7df9a 100644 --- a/src/Aspire.Hosting.Yarp/YarpResource.cs +++ b/src/Aspire.Hosting.Yarp/YarpResource.cs @@ -16,6 +16,11 @@ public class YarpResource : ContainerResource, IResourceWithServiceDiscovery, IC internal List Clusters { get; } = []; + /// + /// Gets or sets the HTTPS host port for the YARP resource. + /// + internal int? HostHttpsPort { get; set; } + /// The name of the resource. public YarpResource(string name) : base(name) { diff --git a/src/Aspire.Hosting.Yarp/YarpResourceExtensions.cs b/src/Aspire.Hosting.Yarp/YarpResourceExtensions.cs index 57cb686fadb..a4416fe29a3 100644 --- a/src/Aspire.Hosting.Yarp/YarpResourceExtensions.cs +++ b/src/Aspire.Hosting.Yarp/YarpResourceExtensions.cs @@ -78,7 +78,13 @@ public static IResourceBuilder AddYarp( // If a TLS certificate is configured, ensure the YARP resource has an HTTPS endpoint and // configure the environment variables to use it. yarpBuilder - .WithHttpsEndpoint(targetPort: HttpsPort) + .WithEndpoint("https", ep => + { + // Create or update the HTTPS endpoint + ep.TargetPort ??= HttpsPort; + ep.UriScheme = "https"; + ep.Port ??= resource.HostHttpsPort; + }, createIfNotExists: true) .WithEnvironment("ASPNETCORE_HTTPS_PORT", resource.GetEndpoint("https").Property(EndpointProperty.Port)) .WithEnvironment("ASPNETCORE_URLS", $"{resource.GetEndpoint("https").Property(EndpointProperty.Scheme)}://*:{resource.GetEndpoint("https").Property(EndpointProperty.TargetPort)};{resource.GetEndpoint("http").Property(EndpointProperty.Scheme)}://*:{resource.GetEndpoint("http").Property(EndpointProperty.TargetPort)}"); } @@ -150,10 +156,9 @@ public static IResourceBuilder WithHostHttpsPort(this IResourceBui { ArgumentNullException.ThrowIfNull(builder); - return builder.WithEndpoint("https", endpoint => - { - endpoint.Port = port; - }, createIfNotExists: false); + builder.Resource.HostHttpsPort = port; + + return builder.WithEndpoint("https", ep => ep.Port = port, createIfNotExists: false); } /// diff --git a/tests/Aspire.Hosting.Yarp.Tests/AddYarpTests.cs b/tests/Aspire.Hosting.Yarp.Tests/AddYarpTests.cs index ca446481df9..d84cb50ef03 100644 --- a/tests/Aspire.Hosting.Yarp.Tests/AddYarpTests.cs +++ b/tests/Aspire.Hosting.Yarp.Tests/AddYarpTests.cs @@ -8,6 +8,7 @@ using Aspire.Hosting.Dcp; using Aspire.Hosting.Tests.Utils; using Aspire.Hosting.Utils; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; namespace Aspire.Hosting.Yarp.Tests; @@ -396,6 +397,57 @@ public async Task VerifyPublishWithStaticFilesGeneratesCorrectDockerfileWithMult await Verify(dockerfile); } + [Fact] + public async Task VerifyWithHostHttpsPortCreatesHttpsEndpointWithSpecifiedPort() + { + using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Run); + + builder.Services.AddSingleton(new TestDeveloperCertificateService( + new List(), + supportsContainerTrust: true, + trustCertificate: true, + tlsTerminate: false)); + + const int httpsPort = 12345; + + var yarp = builder.AddYarp("yarp") + .WithHttpsDeveloperCertificate() + .WithHostHttpsPort(httpsPort); + + using var app = builder.Build(); + var model = app.Services.GetRequiredService(); + + var beforeStartEvent = new BeforeStartEvent(app.Services, model); + await builder.Eventing.PublishAsync(beforeStartEvent); + + var httpsEndpoint = Assert.Single(yarp.Resource.Annotations.OfType(), e => e.Name == "https"); + Assert.Equal(httpsPort, httpsEndpoint.Port); + Assert.Equal("https", httpsEndpoint.UriScheme); + } + + [Fact] + public async Task VerifyWithHostHttpsPortDoesNotCreateHttpsEndpointWithoutCertificateConfiguration() + { + using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Run); + + builder.Services.AddSingleton(new TestDeveloperCertificateService( + new List(), + supportsContainerTrust: true, + trustCertificate: true, + tlsTerminate: false)); + + var yarp = builder.AddYarp("yarp") + .WithHostHttpsPort(12345); + + using var app = builder.Build(); + var model = app.Services.GetRequiredService(); + + var beforeStartEvent = new BeforeStartEvent(app.Services, model); + await builder.Eventing.PublishAsync(beforeStartEvent); + + Assert.DoesNotContain(yarp.Resource.Annotations.OfType(), e => e.Name == "https"); + } + private sealed class TestContainerFilesResource(string name) : ContainerResource(name), IResourceWithContainerFiles { }