@@ -78,20 +78,25 @@ public Task Invoke(HttpContext context)
78
78
private async Task InvokeInternal ( HttpContext context , EnableRateLimitingAttribute ? enableRateLimitingAttribute )
79
79
{
80
80
using var leaseContext = await TryAcquireAsync ( context ) ;
81
- if ( leaseContext . Lease . IsAcquired )
81
+ if ( leaseContext . Lease ? . IsAcquired == true )
82
82
{
83
83
await _next ( context ) ;
84
84
}
85
85
else
86
86
{
87
+ // If the request was canceled, do not call OnRejected, just return.
88
+ if ( leaseContext . RequestRejectionReason == RequestRejectionReason . RequestCanceled )
89
+ {
90
+ return ;
91
+ }
87
92
var thisRequestOnRejected = _defaultOnRejected ;
88
93
RateLimiterLog . RequestRejectedLimitsExceeded ( _logger ) ;
89
94
// OnRejected "wins" over DefaultRejectionStatusCode - we set DefaultRejectionStatusCode first,
90
95
// then call OnRejected in case it wants to do any further modification of the status code.
91
96
context . Response . StatusCode = _rejectionStatusCode ;
92
97
93
98
// If this request was rejected by the endpoint limiter, use its OnRejected if available.
94
- if ( leaseContext . GlobalRejected == false )
99
+ if ( leaseContext . RequestRejectionReason == RequestRejectionReason . EndpointLimiter )
95
100
{
96
101
DefaultRateLimiterPolicy ? policy ;
97
102
// Use custom policy OnRejected if available, else use OnRejected from the Options if available.
@@ -111,15 +116,16 @@ private async Task InvokeInternal(HttpContext context, EnableRateLimitingAttribu
111
116
}
112
117
if ( thisRequestOnRejected is not null )
113
118
{
114
- await thisRequestOnRejected ( new OnRejectedContext ( ) { HttpContext = context , Lease = leaseContext . Lease } , context . RequestAborted ) ;
119
+ // leaseContext.Lease will only be null when the request was canceled.
120
+ await thisRequestOnRejected ( new OnRejectedContext ( ) { HttpContext = context , Lease = leaseContext . Lease ! } , context . RequestAborted ) ;
115
121
}
116
122
}
117
123
}
118
124
119
125
private ValueTask < LeaseContext > TryAcquireAsync ( HttpContext context )
120
126
{
121
127
var leaseContext = CombinedAcquire ( context ) ;
122
- if ( leaseContext . Lease . IsAcquired )
128
+ if ( leaseContext . Lease ? . IsAcquired == true )
123
129
{
124
130
return ValueTask . FromResult ( leaseContext ) ;
125
131
}
@@ -139,14 +145,14 @@ private LeaseContext CombinedAcquire(HttpContext context)
139
145
globalLease = _globalLimiter . AttemptAcquire ( context ) ;
140
146
if ( ! globalLease . IsAcquired )
141
147
{
142
- return new LeaseContext ( ) { GlobalRejected = true , Lease = globalLease } ;
148
+ return new LeaseContext ( ) { RequestRejectionReason = RequestRejectionReason . GlobalLimiter , Lease = globalLease } ;
143
149
}
144
150
}
145
151
endpointLease = _endpointLimiter . AttemptAcquire ( context ) ;
146
152
if ( ! endpointLease . IsAcquired )
147
153
{
148
154
globalLease ? . Dispose ( ) ;
149
- return new LeaseContext ( ) { GlobalRejected = false , Lease = endpointLease } ;
155
+ return new LeaseContext ( ) { RequestRejectionReason = RequestRejectionReason . EndpointLimiter , Lease = endpointLease } ;
150
156
}
151
157
}
152
158
catch ( Exception )
@@ -170,21 +176,30 @@ private async ValueTask<LeaseContext> CombinedWaitAsync(HttpContext context, Can
170
176
globalLease = await _globalLimiter . AcquireAsync ( context , cancellationToken : cancellationToken ) ;
171
177
if ( ! globalLease . IsAcquired )
172
178
{
173
- return new LeaseContext ( ) { GlobalRejected = true , Lease = globalLease } ;
179
+ return new LeaseContext ( ) { RequestRejectionReason = RequestRejectionReason . GlobalLimiter , Lease = globalLease } ;
174
180
}
175
181
}
176
182
endpointLease = await _endpointLimiter . AcquireAsync ( context , cancellationToken : cancellationToken ) ;
177
183
if ( ! endpointLease . IsAcquired )
178
184
{
179
185
globalLease ? . Dispose ( ) ;
180
- return new LeaseContext ( ) { GlobalRejected = false , Lease = endpointLease } ;
186
+ return new LeaseContext ( ) { RequestRejectionReason = RequestRejectionReason . EndpointLimiter , Lease = endpointLease } ;
181
187
}
182
188
}
183
- catch ( Exception )
189
+ catch ( Exception ex )
184
190
{
185
191
endpointLease ? . Dispose ( ) ;
186
192
globalLease ? . Dispose ( ) ;
187
- throw ;
193
+ // Don't throw if the request was canceled - instead log.
194
+ if ( ex is OperationCanceledException && context . RequestAborted . IsCancellationRequested )
195
+ {
196
+ RateLimiterLog . RequestCanceled ( _logger ) ;
197
+ return new LeaseContext ( ) { RequestRejectionReason = RequestRejectionReason . RequestCanceled } ;
198
+ }
199
+ else
200
+ {
201
+ throw ;
202
+ }
188
203
}
189
204
190
205
return globalLease is null ? new LeaseContext ( ) { Lease = endpointLease } : new LeaseContext ( ) { Lease = new DefaultCombinedLease ( globalLease , endpointLease ) } ;
@@ -234,5 +249,8 @@ private static partial class RateLimiterLog
234
249
235
250
[ LoggerMessage ( 2 , LogLevel . Debug , "This endpoint requires a rate limiting policy with name {PolicyName}, but no such policy exists." , EventName = "WarnMissingPolicy" ) ]
236
251
internal static partial void WarnMissingPolicy ( ILogger logger , string policyName ) ;
252
+
253
+ [ LoggerMessage ( 3 , LogLevel . Debug , "The request was canceled." , EventName = "RequestCanceled" ) ]
254
+ internal static partial void RequestCanceled ( ILogger logger ) ;
237
255
}
238
- }
256
+ }
0 commit comments