diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/Authentication/RemoteAppAuthenticationClientOptions.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/Authentication/RemoteAppAuthenticationClientOptions.cs index 0efd829c51..ba43f00084 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/Authentication/RemoteAppAuthenticationClientOptions.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/Authentication/RemoteAppAuthenticationClientOptions.cs @@ -41,5 +41,11 @@ public class RemoteAppAuthenticationClientOptions : AuthenticationSchemeOptions /// services. Requests to authenticate are sent to this endpoint. /// [Required] - public PathString AuthenticationEndpointPath { get; set; } = AuthenticationConstants.DefaultEndpoint; + public PathString AuthenticationEndpointPath + { + get => Path.Path; + set => Path = new(value); + } + + internal RelativePathString Path { get; private set; } = new(AuthenticationConstants.DefaultEndpoint); } diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/Authentication/RemoteAppAuthenticationService.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/Authentication/RemoteAppAuthenticationService.cs index b422bb0a58..1796302772 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/Authentication/RemoteAppAuthenticationService.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/Authentication/RemoteAppAuthenticationService.cs @@ -85,7 +85,7 @@ public async Task AuthenticateAsync(HttpRequest o // that may matter for authentication. Also include the original request path as // as a query parameter so that the ASP.NET app can redirect back to it if an // authentication provider attempts to redirect back to the authenticate URL. - var url = $"{_options.AuthenticationEndpointPath}?{AuthenticationConstants.OriginalUrlQueryParamName}={WebUtility.UrlEncode(originalRequest.GetEncodedPathAndQuery())}"; + var url = $"{_options.Path.Relative}?{AuthenticationConstants.OriginalUrlQueryParamName}={WebUtility.UrlEncode(originalRequest.GetEncodedPathAndQuery())}"; using var authRequest = new HttpRequestMessage(HttpMethod.Get, url); AddHeaders(_options.RequestHeadersToForward, originalRequest, authRequest); diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/RelativePathString.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/RelativePathString.cs new file mode 100644 index 0000000000..e87731fdae --- /dev/null +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/RelativePathString.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.AspNetCore.Http; + +namespace Microsoft.AspNetCore.SystemWebAdapters; + +internal readonly struct RelativePathString +{ + public RelativePathString(PathString path) + { + Path = path; + Relative = "." + path; + } + + public PathString Path { get; } + + /// + /// Use when you want the path to be relative to whatever URI you may combine it with + /// + public string Relative { get; } +} diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/RemoteAppClientOptions.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/RemoteAppClientOptions.cs index a08dac34dd..ee4e5674cb 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/RemoteAppClientOptions.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/RemoteAppClientOptions.cs @@ -12,6 +12,8 @@ namespace Microsoft.AspNetCore.SystemWebAdapters; /// public class RemoteAppClientOptions { + private Uri _remoteAppUrl = null!; + /// /// Gets or sets the header used to store the API key /// @@ -28,7 +30,27 @@ public class RemoteAppClientOptions /// Gets or sets the remote app url /// [Required] - public Uri RemoteAppUrl { get; set; } = null!; + public Uri RemoteAppUrl + { + get => _remoteAppUrl; + set + { + if (value is null) + { + throw new ArgumentNullException(nameof(RemoteAppUrl)); + } + + // Path must end in '/' so that it will combine correctly with subpaths + if (!value.AbsolutePath.EndsWith("/")) + { + var builder = new UriBuilder(value); + builder.Path += "/"; + value = builder.Uri; + } + + _remoteAppUrl = value; + } + } /// /// Gets or sets an to use for making requests to the remote app. diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/SessionState/RemoteSession/RemoteAppSessionStateClientOptions.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/SessionState/RemoteSession/RemoteAppSessionStateClientOptions.cs index f2b7159a59..34ed3a5034 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/SessionState/RemoteSession/RemoteAppSessionStateClientOptions.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/SessionState/RemoteSession/RemoteAppSessionStateClientOptions.cs @@ -2,13 +2,20 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Http; namespace Microsoft.AspNetCore.SystemWebAdapters.SessionState.RemoteSession; public class RemoteAppSessionStateClientOptions { [Required] - public string SessionEndpointPath { get; set; } = SessionConstants.SessionEndpointPath; + public PathString SessionEndpointPath + { + get => Path.Path; + set => Path = new(value); + } + + internal RelativePathString Path { get; private set; } = new(SessionConstants.SessionEndpointPath); /// /// Gets or sets the cookie name that the ASP.NET framework app is expecting to hold the session id diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/SessionState/RemoteSession/RemoteAppSessionStateManager.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/SessionState/RemoteSession/RemoteAppSessionStateManager.cs index 4095231af3..fdaea50d42 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/SessionState/RemoteSession/RemoteAppSessionStateManager.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/SessionState/RemoteSession/RemoteAppSessionStateManager.cs @@ -73,7 +73,7 @@ private async Task GetSessionDataAsync(string? sessionId, bool re { // The request message is manually disposed at a later time #pragma warning disable CA2000 // Dispose objects before losing scope - var req = new HttpRequestMessage(HttpMethod.Get, _options.SessionEndpointPath); + var req = new HttpRequestMessage(HttpMethod.Get, _options.Path.Relative); #pragma warning restore CA2000 // Dispose objects before losing scope AddSessionCookieToHeader(req, sessionId); @@ -112,7 +112,7 @@ private async Task GetSessionDataAsync(string? sessionId, bool re /// private async Task SetOrReleaseSessionData(ISessionState? state, CancellationToken cancellationToken) { - using var req = new HttpRequestMessage(HttpMethod.Put, _options.SessionEndpointPath); + using var req = new HttpRequestMessage(HttpMethod.Put, _options.Path.Relative); if (state is not null) { diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.FrameworkServices/RemoteModule.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.FrameworkServices/RemoteModule.cs index 4781652e98..518e28177c 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.FrameworkServices/RemoteModule.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.FrameworkServices/RemoteModule.cs @@ -50,11 +50,14 @@ void IHttpModule.Dispose() void IHttpModule.Init(HttpApplication context) { + var appRelativePath = $"~{Path}"; + context.PostMapRequestHandler += (s, _) => { var context = ((HttpApplication)s).Context; - if (!string.Equals(context.Request.Path, Path, StringComparison.Ordinal)) + // Compare against the AppRelativeCurrentExecutionFilePath to account for potential virtual directories + if (!string.Equals(context.Request.AppRelativeCurrentExecutionFilePath, appRelativePath, StringComparison.Ordinal)) { return; } diff --git a/test/Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests/RemoteAppClientOptionsTests.cs b/test/Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests/RemoteAppClientOptionsTests.cs index ec7c8a1a41..c6f8771019 100644 --- a/test/Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests/RemoteAppClientOptionsTests.cs +++ b/test/Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests/RemoteAppClientOptionsTests.cs @@ -35,7 +35,11 @@ public void VerifyIsCalled(string apiKeyHeader, string apiKey, string remoteAppU { options.ApiKey = apiKey; options.ApiKeyHeader = apiKeyHeader; - options.RemoteAppUrl = (remoteAppUrl is null ? null : new Uri(remoteAppUrl, UriKind.Absolute))!; + + if (remoteAppUrl is not null) + { + options.RemoteAppUrl = new Uri(remoteAppUrl, UriKind.Absolute); + } })); using var serviceProvider = services.BuildServiceProvider();