Aspnetcore: Add support for specifying the PathBase for all requests

Created on 29 Jun 2017  路  16Comments  路  Source: dotnet/aspnetcore

As a follow up from https://github.com/aspnet/Hosting/issues/815, we want to allow specifying the path base as a host setting. Hosting should use this to call UsePathBase as the first middleware in the pipeline. IISIntegration does something similar with the APPL_PATH env variable : https://github.com/aspnet/IISIntegration/blob/51554566532237ef76cb105ec5b3f7a5050b9ba4/src/Microsoft.AspNetCore.Server.IISIntegration/WebHostBuilderIISExtensions.cs#L19.

affected-few area-hosting enhancement severity-minor

Most helpful comment

Just got burned by this, specifically proxy trimming the path. Sorry I have nothing more valuable to add other than feedback from the wild.

All 16 comments

You want to be able to switch between WebListener ( or any host) and Kestrel which share the same startup before startup is loaded.. Having If (KestralLoaded) in startup is pretty damn ugly .. and makes testing a pain , i dont think there should be any Host ( including Kestrel) specific stuff in Startup .

Using IStartupFilter is pretty ugly and having 2 methods for the 2 web hosts is pretty poor . As I mentioned before other systems may do a lot of work before startup eg console hosts , hosted by SF etc etc . Core and web listener supports this nicely but Kestrel does not. eg Hosting is a ASP.Net concept in startup I think the port/url hosting needs to be done before Startup .. Sure Startup should have access to this but it should be set before.

I'm not sure what you're referring to but there will be a use path base on the IWebHostBuilder like the method suggests. I don't know what more you're asking for.

@davidfowl For testing this change, I believe I need to send a request to the application to verify that the HttpContext properly contains the PathBase.

Firstly, is there a better way to test this?

If this is correct, what type of test would this be? It seems to extend past the WebHosts tests because it requires that a request be sent to a server.

@jkotalik Whats the issue with sending a request?

Maybe it's a good idea to also make the use of proxy forwarding HTTP headers configurable for the hosting environment. Because these headers are provided by the hosting environment and could be different per host. The application shouldn't need to care about what headers the proxy provides.

My applications currently draw the application path base from an environment variable provided by my own hosting environment and put it into UsePathBase, and they also call UseForwardedHeaders because the hosting environment works that way.

But I can't influence third-party applications. They might stop working in some hosting environments.

I just stumbled on this issue again; what is the actual goal of this? To move the configuration of the path base into the WebHostBuilder, to ensure that it is always done at the very beginning of the middleware pipeline? And so that people can configure it through the host builder configuration (e.g. using environment variables, command line arguments, etc.?)

@poke yes that was the intent

I see, I might have the chance to work on this then, seeing that this is not planned by someone else at this point.

Does the solution include any support for allowing to dynamically set the path base when sitting behaind a reverse proxy? In one of my applications, I currently use a custom X-Forwarded-Path HTTP header to dynamically configure the path base (along with the other X-Forwarded headers) to completely uncouple the configuration from the application, moving it into the reverse proxy configuration.

I don鈥檛 assume that we would want to move such a functionality into the webhost but keep it as custom middleware then, right (Just like the UseForwardedHeaders middleware)? In that case, we would have to make sure that the stuff in the webhost will not conflict here and that you could still overwrite the path base properly in user middleware.

X-Forwarded-Path would be an interesting add to UseForwardedHeaders, though it appears to be far less common.

The ask for Hosting is only for a static config based mapping for basic reverse proxy scenarios.

That said, I've seen some variants of this request in forums that UsePathBase does not cover. If the proxy trims or prepends the path then UsePathBase doesn't work, it relies on an unchanged path.

Examples:

A basic pass through:
Public Path: /foo/api/1
Proxy forwards: /foo/api/1
Fixup needed: UsePathBase(/foo), Path becomes: /api/1 and PathBase becomes: /foo

Variant 1: Proxy trims the path
Public Path: /foo/api/1
Proxy forwards /api/1
Fixup needed: Set PathBase to /foo, leave Path alone

Variant 2: Proxy prepends path
Public Path: /api/1
Proxy forwards /foo/api/1
Fixup needed: Remove /foo from Path, don't set PathBase

UsePathBase might need some overloads to handle these: Transfer, Set, Trim. I don't know how easily we could get those all into hosting config unless they were all separate parameters.

Just got burned by this, specifically proxy trimming the path. Sorry I have nothing more valuable to add other than feedback from the wild.

I'm pretty sure this is the inverse of PathBase. I'm having to add it back in order to get ~ to work.

I'm taking http://staging.hanselman.com/blog and pointing it to http://hanselmanblog.azurewebsites.net so I need to add it BACK not strip it. Per https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-2.2#deal-with-path-base-and-proxies-that-change-the-request-path

app.Use((context, next) =>
{
     context.Request.PathBase = new PathString(/blog);
     return next.Invoke();
});

I don't have a solution for the underlying AspNetCore issue, however I found a viable server-side workaround until a DotNet Core solution is found.

If a unique proxy_pass location block is created in the proxy server for every single DotNet Route, DotNet will be able to respond as expected.

This of course requires close coordination between server admins and app devs, or a script which dynamically modifies location blocks with each iterative update of the backing DotNet application.

While certainly is not a viable long-term solution, it does ensure that in the interim, while this bug is being addressed, DotNet APIs will "play nice" behind path-rewriting proxies.

I encountered this issue today, and I solved it by adding a custom middleware which looks at the X-Path-Base header that I configured my reverse proxy to set, which sets the PathBase and Path based on the value in that header (shamelessly copied the implementation from UsePathBaseMiddleware.cs). Would it be possible to extend the ForwardedHeadersMiddleware and its corresponding Options class with something like this? I can create a pull request if this looks like a sound solution to the problem.

c# app.Use(async (context, next) => { // TODO Check if host is valid based on hosts in ForwardedHeadersOptions if (context.Request.Headers.TryGetValue("X-Path-Base", out var pathBase) && context.Request.Path.StartsWithSegments(pathBase.Last(), out var matchedPath, out var remainingPath)) { try { var originalPath = context.Request.Path; var originalPathBase = context.Request.PathBase; context.Request.PathBase = matchedPath; context.Request.Path = remainingPath; await next(); } finally { context.Request.PathBase = new PathString(matchedPath); context.Request.Path = new PathString(remainingPath); } } else { await next(); } });

Was this page helpful?
0 / 5 - 0 ratings