dotnet / dotnet/aspnetcore

Rate Limit for Kestrel - Design mechanism to apply back pressure to accepting connections

Open
#13,295 22 comments 5 reactions 0 assignees View on GitHub
affected-very-few area-middleware area-networking blocked enhancement feature-rate-limit feature-yarp Needs: Design Priority:1 severity-nice-to-have 🥌 Bedrock
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 10h
Merged PRs (30d)
281

Description

### Is your feature request related to a problem? Please describe.
To match functionality with WCF on .NET Framework, CoreWCF needs to be able to control the number of pending accepts. When a client connects to WCF using the NetTcp transport, there is a handshake that needs to be completed which includes authentication and security upgrade negotiation. After a connection has been established and while the handshake is ongoing, a connection is considered to be a pending connection. WCF controls the maximum number of connections which are in the pending connection state by controlling the maximum number of pending accepts to never be more than how many more pending connections are possible. For example, if the maximum number of pending connections is configured to be 100 and there are currently 95 pending connections, we limit the number of pending accepts to be no more than 5.

### Describe the solution you'd like
I believe there should be a generic throttling interface that can be added by DI for use at any layer within an app, and a derived interface (which adds no extra methods) which is used at the transport layer. The two interfaces would be defined as:
```c#
public interface IThrottle
{
ValueTask AcquireAsync(CancellationToken cancellationToken = default(CancellationToken));
void Release();
}

public interface ITransportThrottle : IThrottle { }
```
When calling AcquireAsync, one of four things can happen.
1. The returned ValueTask is completed with a value of true meaning the throttle was acquired.
2. The returned ValueTask is not completed and the call goes async. When a throttle can be acquired, the ValueTask is completed with a value of true meaning the throttle was acquired.
3. The returned ValueTask is completed with a value of false meaning the throttle can not be acquired immediately. This would be used for a throttle implementation which is used when you want to fail immediately such as returning a 503 throttled HTTP response.
4. The returned ValueTak is not completed and the call goes async. If the cancellation token is cancelled, the ValueTask is completed with a value of false meaning the throttle was not acquired. This would be used for scenarios where waiting for a throttle can time out or when shutting down the application and you need to complete the pending AcquireAsync call for cleanup.

In the code which calls Accept on the listening socket, if an ITransportThrottleexists in DI, in a loop it acquires the throttle and adds another pending accept until ITransportThrottle.AcquireAsync returns a non-completed ValueTask. It would be up to the implementation of the ITransportThrottleto ensure there's a sensible number of pending accepts. Basically the number of pending accepts is completely controlled by the ITransportThrottleif it exists.

Edit: Changed IServiceThrottle to ITransportThrottle

Contributor guide

Open the contributing guide

Research direction

Start by reviewing Kestrel's listening-socket accept path and how services are added through DI. Compare the proposed IThrottle and ITransportThrottle lifecycle with the pending-connection and pending-accept behavior described here. Done means the transport can use an injected throttle to control pending accepts while handling acquisition, release, cancellation, and immediate failure consistently.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
backend, networking
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.