diff --git a/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttpModules.cs b/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttpModules.cs index c555ea02b..64cf0f49a 100644 --- a/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttpModules.cs +++ b/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttpModules.cs @@ -316,6 +316,41 @@ void IHttpModule.Dispose() } #endregion } + public class GXVirtualPathCasingModule : IHttpModule + { + private static readonly IGXLogger log = GXLoggerFactory.GetLogger(); + + public void Init(HttpApplication context) + { + context.BeginRequest += OnBeginRequest; + } + + public void Dispose() + { + } + + private static void OnBeginRequest(object sender, EventArgs e) + { + HttpContext context = ((HttpApplication)sender).Context; + string appPath = HttpRuntime.AppDomainAppVirtualPath; + if (string.IsNullOrEmpty(appPath) || appPath == "/") + return; + + string rawUrl = context.Request.RawUrl; + if (rawUrl == null) + return; + + if (!rawUrl.StartsWith(appPath, StringComparison.Ordinal) && + rawUrl.StartsWith(appPath, StringComparison.OrdinalIgnoreCase)) + { + string canonical = appPath + rawUrl.Substring(appPath.Length); + GXLogging.Debug(log, "Redirecting non-canonical app path '", rawUrl, "' to '", canonical, "'"); +#pragma warning disable SCS0027 // Open redirect: target is built from AppDomainAppVirtualPath (server config) and the original request path, always same-origin relative URL + context.Response.RedirectPermanent(canonical, true); +#pragma warning restore SCS0027 + } + } + } public class GXRewriter : IHttpModule { private static readonly IGXLogger log = GXLoggerFactory.GetLogger();