-
Notifications
You must be signed in to change notification settings - Fork 10.3k
Add Route Short Circuit middleware #46071
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Thanks for contacting us. We're moving this issue to the |
Thank you for submitting this for API review. This will be reviewed by @dotnet/aspnet-api-review at the next meeting of the ASP.NET Core API Review group. Please ensure you take a look at the API review process documentation and ensure that:
|
Could this be extended to support configuring file extensions too? I'm thinking of a use case where you get bots probing for PHP stuff and WordPress sites etc., and you could just blanket short-circuit all requests for |
I wonder if this should be built on top of routing instead. This middleware would the short circuit if the appropriate endpoint matched with specific metadata. Another ref count for #43642. Strawman would look like; app.MapMetadata("/{path}.php").ShortCircuit(); // Add the short circuit metadata to this route pattern
// This middleware short circuits the request requests
app.UseRouteShortCircuit(); |
Why even have UseRouteShortCircuit? Why not have UseRouting do it? The original intent was to avoid the costs of static files lookups, route tables, logging & telemetry, etc.. Building into the route table limits the effectiveness of the feature, though it does make match definitions easier. |
Agreed @Tratcher! Something we can measure though, right? |
@davidfowl Routing comes later in the pipeline, right? After authentication, throttling, telemetry, etc. Ideally, we'd want to zero the costs for these requests. We don't need to attempt to authenticate them, we don't need to consider throttling them, we are not interested in capturing telemetry for them. Short circuiting as soon as the requests hits the middleware pipeline does that. |
@tekian these days we're putting routing almost first, second only to exception handlers. Auth and throttling come later because they want to consider which endpoint the request was routed to. Telemetry is custom middleware in this case? That goes wherever you put it. |
@Tratcher I'm not aware of a change that would put routing almost first. Can you share more information on this? Usually what I have seen is that services assemble route-agnostic middlewares in-between exception handling and routing. That way, they can apply shared policies such as |
When you use the new WebApplicationBuilder class it automatically adds UseDeveloperExceptionPage and UseRouting at the start of the pipeline if you did not specify them. aspnetcore/src/DefaultBuilder/src/WebApplicationBuilder.cs Lines 148 to 165 in 83d6c56
I understand wanting to minimize the costs. We should see what the cost of moving routing up front would be for apps like yours to see if this approach makes sense. |
@tekian That makes sense, and it sounds like avoiding those things would still happen. |
We could also do both:
|
When do you choose one over the other? |
Middleware: Use when you have expensive things before routing and only need to do simple matches. The permutation that doesn't work is needing to do complex matching before routing. |
I need to see some examples and benchmarks to be convinced we need both. |
@Tratcher Thanks! Looking into the code, even if you use |
Here is another way how to look at it: It may not be .NET's code that is slow. It may be a 3P library that provides a middleware that isn't particularly efficient for all requests. 😁 Having two ways to configure the same thing doesn't seem right. How about:
@davidfowl Is that the way you were thinking about it? |
How is this different from moving UseRouting earlier? It seems like it would have to do the same work. |
Oh I see what you mean. We'd advise to add routing as one of the first middlewares, claiming and assuming that the overhead to compute a route is negligent even for cases where we want to short circuit. And we'd sequence the rest of the middlewares after it, even those that are agnostic of the path. |
API Review Notes:
API Approved! Example Usage: app.MapGet("/favicon.ico", httpContext =>
{
// httpContext.Response.StatusCode will default to 404 here.
httpContext.RequestServices.GetService<ILogger<MyThing>>().LogTrace("Skipped favicon.ico");
}).ShortCircuit();
app.MapGet("/short-circuit-prefix/**", httpContext => { .. }).ShortCircuit();
// So /favicon.ico/test/extra/path would also be rejected
// MapRejects 404s and implies ShortCircuit()
app.MapShortCircuit("/favicon.ico", "/short-circuit-prefix", "/another-prefix"); Project/Assembly: Microsoft.AspNetCore.Routing
namespace Microsoft.AspNetCore.Builder;
+ public static class RouteShortCircuitEndpointConventionBuilderExtensions
+ {
+ public static IEndpointConventionBuilder ShortCircuit(this IEndpointConventionBuilder builder) { }
+ }
namespace Microsoft.AspNetCore.Routing;
+ public static class RouteShortCircuitEndpointRouteBuilderExtensions
+ {
+ public static IEndpointConventionBuilder MapShortCircuit(this IEndpointConventionBuilder builder, params string[] routePrefixes);
+ } |
I still need to work out what metrics this will produce, and how that would be configurable. |
Shouldn't the |
You're right, the
This was intentional so that you could add additional route constraints like host filtering. |
@Tratcher I'm interested in doing this one. Would you accept a PR? |
Sure, but hold off a little longer, I still need to work out the requirements for logging/metrics. |
I've made a separate proposal for metrics (#46361) that's generalized to all of routing. I'll see if that's acceptable to people. |
As for this design, I'm still a little leary about ShortCircuit and MapShortCircuit being hardcoded to 404 responses. Executing a route immediately does not necessarily imply 404, especially with a RequestDelegate and ShortCircuit, that could generate any kind of response. @halter73 I think we should amend both of these to take a status code: app.MapGet("/favicon.ico", httpContext =>
{
httpContext.RequestServices.GetService<ILogger<MyThing>>().LogTrace("Skipped favicon.ico");
}).ShortCircuit(404);
app.MapGet("/short-circuit-prefix/**", httpContext => { .. }).ShortCircuit(404);
// So /favicon.ico/test/extra/path would also be rejected
// MapShortCircuit implies ShortCircuit(status)
app.MapShortCircuit(404, "/favicon.ico", "/short-circuit-prefix", "/another-prefix"); Project/Assembly: Microsoft.AspNetCore.Routing
namespace Microsoft.AspNetCore.Builder;
+ public static class RouteShortCircuitEndpointConventionBuilderExtensions
+ {
+ public static IEndpointConventionBuilder ShortCircuit(this IEndpointConventionBuilder builder, int statusCode) { }
+ }
namespace Microsoft.AspNetCore.Routing;
+ public static class RouteShortCircuitEndpointRouteBuilderExtensions
+ {
+ public static IEndpointConventionBuilder MapShortCircuit(this IEndpointRouteBuilder builder, int statusCode, params string[] routePrefixes);
+ } |
@Tratcher Did you mean to write |
Uh, sure, that one 😆 |
Thank you for submitting this for API review. This will be reviewed by @dotnet/aspnet-api-review at the next meeting of the ASP.NET Core API Review group. Please ensure you take a look at the API review process documentation and ensure that:
|
API Review notes:
app.MapShortCircuit(404, "prefix1", "prefix2");
// vs.
app.MapShortCircuit(new[] { "prefix1", "prefix2" }, 404);
// vs.
// public static IEndpointConventionBuilder MapShortCircuit(this IEndpointRouteBuilder builder, params (int StatusCode, string Prefix)[] tuples)
app.MapShortCircuit(("prefix1", 404), ("prefix2", 400));
// vs. Require one at a time. public static IEndpointConventionBuilder MapShortCircuit(this IEndpointRouteBuilder builder, string prefix, int statusCode = 404)
app.MapShortCircuit("prefix1");
app.MapShortCircuit("prefix2", 400);
Example Usage: app.MapGet("/favicon.ico", httpContext =>
{
// httpContext.Response.StatusCode will not be set by default before running this unless a parameter is passed
httpContext.Response.StatusCode = 404;
httpContext.RequestServices.GetService<ILogger<MyThing>>().LogTrace("Skipped favicon.ico");
}).ShortCircuit();
app.MapGet("/favicon2.ico", httpContext =>
{
// httpContext.Response.StatusCode will be set to 404 here.
httpContext.RequestServices.GetService<ILogger<MyThing>>().LogTrace("Skipped favicon2.ico");
}).ShortCircuit(404);
app.MapGet("/favicon3.ico", httpContext =>
{
// This blows up because of RequireAuthorization()
}).RequireAuthorization().ShortCircuit(404);
app.MapGet("/short-circuit-prefix/**", httpContext => { .. }).ShortCircuit();
// So /favicon.ico/test/extra/path would also be rejected
// MapShortCircuit sets the specified status code and implies ShortCircuit()
app.MapShortCircuit(404, "/favicon.ico", "/short-circuit-prefix", "/another-prefix");
// May not work, but you can get route prefixes from config
app.MapShortCircuit(404, app.Config["MyShortCircuits"].GetChildren()...);
API Approved! Project/Assembly: Microsoft.AspNetCore.Routing
namespace Microsoft.AspNetCore.Builder;
+ public static class RouteShortCircuitEndpointConventionBuilderExtensions
+ {
+ public static IEndpointConventionBuilder ShortCircuit(this IEndpointConventionBuilder builder, int? statusCode = null) { }
+ }
namespace Microsoft.AspNetCore.Routing;
+ public static class RouteShortCircuitEndpointRouteBuilderExtensions
+ {
+ public static IEndpointConventionBuilder MapShortCircuit(this IEndpointRouteBuilder builder, int statusCode, params string[] routePrefixes);
+ } |
Maybe, but MapGet(...).ShortCircuit() seems adequate. Isn't there also a way to add a method constraint using the IEndpointConventionBuilder returned from MapShortCircuit? |
@javiercn proposed an additional API where the implementation is moved out of the route middleware into its own.
Pros:
Cons:
|
Thank you for submitting this for API review. This will be reviewed by @dotnet/aspnet-api-review at the next meeting of the ASP.NET Core API Review group. Please ensure you take a look at the API review process documentation and ensure that:
|
API Review Notes:
API update rejected. Original API approved with no updated! |
Background and Motivation
Browsers and bots often probe servers for well known paths like favicon.ico. Normally the middleware pipeline runs to completion before rejecting a request as 404.
Services can reduce resource usage and log noise by filtering out known requests early in the pipeline. We can provide a middleware to do this efficiently.
Proposed API
Usage Examples
Alternative:
Build this into UseRouting (#43642) and have it terminate immediately with a 404 if such an endpoint is matched.
We could also allow short circuiting to specify an endpoint/delegate to execute to produce a custom response.
The text was updated successfully, but these errors were encountered: