Skip to content

Commit d784a5b

Browse files
committed
Resolve conflict
1 parent c3f903f commit d784a5b

19 files changed

+1137
-1
lines changed

src/Http/Http/perf/Microbenchmarks/Microsoft.AspNetCore.Http.Microbenchmarks.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<ItemGroup>
1212
<Reference Include="BenchmarkDotNet" />
1313
<Reference Include="Microsoft.AspNetCore.Http" />
14+
<Reference Include="Microsoft.AspNetCore.Http.Extensions" />
1415
<Compile Include="$(SharedSourceRoot)BenchmarkRunner\*.cs" />
1516
</ItemGroup>
1617

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using BenchmarkDotNet.Attributes;
5+
using Microsoft.AspNetCore.Http.Timeouts;
6+
using Microsoft.Extensions.Logging.Abstractions;
7+
using Microsoft.Extensions.Options;
8+
9+
namespace Microsoft.AspNetCore.Http;
10+
11+
public class RequestTimeoutsMiddlewareBenchmark
12+
{
13+
RequestTimeoutsMiddleware _middlewareWithNoTimeout;
14+
RequestTimeoutsMiddleware _middleware;
15+
RequestTimeoutsMiddleware _middlewareWithThrow;
16+
17+
[GlobalSetup]
18+
public void GlobalSetup()
19+
{
20+
_middlewareWithNoTimeout = new RequestTimeoutsMiddleware(
21+
async context => { await Task.Yield(); },
22+
new CancellationTokenLinker(),
23+
NullLogger<RequestTimeoutsMiddleware>.Instance,
24+
Options.Create(new RequestTimeoutOptions()));
25+
26+
_middleware = new RequestTimeoutsMiddleware(
27+
async context => { await Task.Yield(); },
28+
new CancellationTokenLinker(),
29+
NullLogger<RequestTimeoutsMiddleware>.Instance,
30+
Options.Create(new RequestTimeoutOptions
31+
{
32+
DefaultPolicy = new RequestTimeoutPolicy
33+
{
34+
Timeout = TimeSpan.FromMilliseconds(200)
35+
},
36+
Policies =
37+
{
38+
["policy1"] = new RequestTimeoutPolicy { Timeout = TimeSpan.FromMilliseconds(200)}
39+
}
40+
}));
41+
42+
_middlewareWithThrow = new RequestTimeoutsMiddleware(
43+
async context =>
44+
{
45+
await Task.Delay(TimeSpan.FromMicroseconds(2));
46+
context.RequestAborted.ThrowIfCancellationRequested();
47+
},
48+
new CancellationTokenLinker(),
49+
NullLogger<RequestTimeoutsMiddleware>.Instance,
50+
Options.Create(new RequestTimeoutOptions
51+
{
52+
DefaultPolicy = new RequestTimeoutPolicy
53+
{
54+
Timeout = TimeSpan.FromMicroseconds(1)
55+
}
56+
}));
57+
}
58+
59+
[Benchmark]
60+
public async Task NoMetadataNoDefault()
61+
{
62+
var context = CreateHttpContext(new Endpoint(null, null, null));
63+
await _middlewareWithNoTimeout.Invoke(context);
64+
}
65+
66+
[Benchmark]
67+
public async Task DefaultTimeout()
68+
{
69+
var context = CreateHttpContext(new Endpoint(null, null, null));
70+
71+
await _middleware.Invoke(context);
72+
}
73+
74+
[Benchmark]
75+
public async Task DefaultTimeoutOverridenByDisable()
76+
{
77+
var context = CreateHttpContext(new Endpoint(
78+
null,
79+
new EndpointMetadataCollection(new DisableRequestTimeoutAttribute()),
80+
null));
81+
82+
await _middleware.Invoke(context);
83+
}
84+
85+
[Benchmark]
86+
public async Task TimeoutMetadata()
87+
{
88+
var context = CreateHttpContext(new Endpoint(
89+
null,
90+
new EndpointMetadataCollection(new RequestTimeoutAttribute(200)),
91+
null));
92+
93+
await _middleware.Invoke(context);
94+
}
95+
96+
[Benchmark]
97+
public async Task NamedPolicyMetadata()
98+
{
99+
var context = CreateHttpContext(new Endpoint(
100+
null,
101+
new EndpointMetadataCollection(new RequestTimeoutAttribute("policy1")),
102+
null));
103+
104+
await _middleware.Invoke(context);
105+
}
106+
107+
[Benchmark]
108+
public async Task TimeoutFires()
109+
{
110+
var context = CreateHttpContext(new Endpoint(null, null, null));
111+
112+
await _middlewareWithThrow.Invoke(context);
113+
}
114+
115+
private HttpContext CreateHttpContext(Endpoint endpoint)
116+
{
117+
var context = new DefaultHttpContext();
118+
context.SetEndpoint(endpoint);
119+
120+
var cts = new CancellationTokenSource();
121+
context.RequestAborted = cts.Token;
122+
123+
return context;
124+
}
125+
126+
private sealed class Options : IOptionsMonitor<RequestTimeoutOptions>
127+
{
128+
private readonly RequestTimeoutOptions _options;
129+
130+
private Options(RequestTimeoutOptions options)
131+
{
132+
_options = options;
133+
}
134+
135+
public static Options Create(RequestTimeoutOptions options)
136+
{
137+
return new Options(options);
138+
}
139+
140+
public RequestTimeoutOptions CurrentValue => _options;
141+
142+
public RequestTimeoutOptions Get(string name) => _options;
143+
144+
public IDisposable OnChange(Action<RequestTimeoutOptions, string> listener)
145+
{
146+
return default;
147+
}
148+
}
149+
}

src/Http/Http/src/Microsoft.AspNetCore.Http.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
</PropertyGroup>
1313

1414
<ItemGroup>
15+
<Compile Include="$(SharedSourceRoot)CancellationTokenSourcePool.cs" />
1516
<Compile Include="$(SharedSourceRoot)CopyOnWriteDictionary\*.cs" LinkBase="Shared"/>
1617
<Compile Include="$(SharedSourceRoot)ValueTaskExtensions\**\*.cs" LinkBase="Shared"/>
1718
<Compile Include="..\..\Shared\StreamCopyOperationInternal.cs" Link="Internal\StreamCopyOperationInternal.cs" />
@@ -25,6 +26,7 @@
2526
<ItemGroup>
2627
<Reference Include="Microsoft.AspNetCore.Connections.Abstractions" />
2728
<Reference Include="Microsoft.AspNetCore.Http.Abstractions" />
29+
<Reference Include="Microsoft.AspNetCore.Http.Extensions" />
2830
<Reference Include="Microsoft.AspNetCore.WebUtilities" />
2931
<Reference Include="Microsoft.Extensions.ObjectPool" />
3032
<Reference Include="Microsoft.Extensions.Options" />
Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,38 @@
11
#nullable enable
2+
Microsoft.AspNetCore.Builder.RequestTimeoutsIApplicationBuilderExtensions
3+
Microsoft.AspNetCore.Builder.RequestTimeoutsIEndpointConventionBuilderExtensions
24
Microsoft.AspNetCore.Http.BindingAddress.IsNamedPipe.get -> bool
3-
Microsoft.AspNetCore.Http.BindingAddress.NamedPipeName.get -> string!
5+
Microsoft.AspNetCore.Http.BindingAddress.NamedPipeName.get -> string!
6+
Microsoft.AspNetCore.Http.Timeouts.DisableRequestTimeoutAttribute
7+
Microsoft.AspNetCore.Http.Timeouts.DisableRequestTimeoutAttribute.DisableRequestTimeoutAttribute() -> void
8+
Microsoft.AspNetCore.Http.Timeouts.IHttpRequestTimeoutFeature
9+
Microsoft.AspNetCore.Http.Timeouts.IHttpRequestTimeoutFeature.DisableTimeout() -> void
10+
Microsoft.AspNetCore.Http.Timeouts.IHttpRequestTimeoutFeature.RequestTimeoutToken.get -> System.Threading.CancellationToken
11+
Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutAttribute
12+
Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutAttribute.PolicyName.get -> string?
13+
Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutAttribute.RequestTimeoutAttribute(int milliseconds) -> void
14+
Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutAttribute.RequestTimeoutAttribute(string! policyName) -> void
15+
Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutAttribute.Timeout.get -> System.TimeSpan?
16+
Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutOptions
17+
Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutOptions.AddPolicy(string! policyName, Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutPolicy! policy) -> Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutOptions!
18+
Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutOptions.AddPolicy(string! policyName, System.TimeSpan timeout) -> Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutOptions!
19+
Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutOptions.DefaultPolicy.get -> Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutPolicy?
20+
Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutOptions.DefaultPolicy.set -> void
21+
Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutOptions.Policies.get -> System.Collections.Generic.IDictionary<string!, Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutPolicy!>!
22+
Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutOptions.RequestTimeoutOptions() -> void
23+
Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutPolicy
24+
Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutPolicy.RequestTimeoutPolicy() -> void
25+
Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutPolicy.Timeout.get -> System.TimeSpan?
26+
Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutPolicy.Timeout.init -> void
27+
Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutPolicy.TimeoutStatusCode.get -> int?
28+
Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutPolicy.TimeoutStatusCode.init -> void
29+
Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutPolicy.WriteTimeoutResponse.get -> Microsoft.AspNetCore.Http.RequestDelegate?
30+
Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutPolicy.WriteTimeoutResponse.init -> void
31+
Microsoft.Extensions.DependencyInjection.RequestTimeoutsIServiceCollectionExtensions
32+
static Microsoft.AspNetCore.Builder.RequestTimeoutsIApplicationBuilderExtensions.UseRequestTimeouts(this Microsoft.AspNetCore.Builder.IApplicationBuilder! builder) -> Microsoft.AspNetCore.Builder.IApplicationBuilder!
33+
static Microsoft.AspNetCore.Builder.RequestTimeoutsIEndpointConventionBuilderExtensions.DisableRequestTimeout(this Microsoft.AspNetCore.Builder.IEndpointConventionBuilder! builder) -> Microsoft.AspNetCore.Builder.IEndpointConventionBuilder!
34+
static Microsoft.AspNetCore.Builder.RequestTimeoutsIEndpointConventionBuilderExtensions.WithRequestTimeout(this Microsoft.AspNetCore.Builder.IEndpointConventionBuilder! builder, Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutPolicy! policy) -> Microsoft.AspNetCore.Builder.IEndpointConventionBuilder!
35+
static Microsoft.AspNetCore.Builder.RequestTimeoutsIEndpointConventionBuilderExtensions.WithRequestTimeout(this Microsoft.AspNetCore.Builder.IEndpointConventionBuilder! builder, string! policyName) -> Microsoft.AspNetCore.Builder.IEndpointConventionBuilder!
36+
static Microsoft.AspNetCore.Builder.RequestTimeoutsIEndpointConventionBuilderExtensions.WithRequestTimeout(this Microsoft.AspNetCore.Builder.IEndpointConventionBuilder! builder, System.TimeSpan timeout) -> Microsoft.AspNetCore.Builder.IEndpointConventionBuilder!
37+
static Microsoft.Extensions.DependencyInjection.RequestTimeoutsIServiceCollectionExtensions.AddRequestTimeouts(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
38+
static Microsoft.Extensions.DependencyInjection.RequestTimeoutsIServiceCollectionExtensions.AddRequestTimeouts(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action<Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutOptions!>! configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Microsoft.AspNetCore.Internal;
5+
6+
namespace Microsoft.AspNetCore.Http.Timeouts;
7+
8+
internal sealed class CancellationTokenLinker : ICancellationTokenLinker
9+
{
10+
private readonly CancellationTokenSourcePool _ctsPool = new();
11+
12+
public (CancellationTokenSource linkedCts, CancellationTokenSource timeoutCts) GetLinkedCancellationTokenSource(HttpContext httpContext, CancellationToken originalToken, TimeSpan timeSpan)
13+
{
14+
var timeoutCts = _ctsPool.Rent();
15+
timeoutCts.CancelAfter(timeSpan);
16+
httpContext.Response.RegisterForDispose(timeoutCts);
17+
var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(originalToken, timeoutCts.Token);
18+
return (linkedCts, timeoutCts);
19+
}
20+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Microsoft.AspNetCore.Http.Timeouts;
5+
6+
/// <summary>
7+
/// Metadata that disables request timeouts limiting on an endpoint.
8+
/// </summary>
9+
/// <remarks>
10+
/// Completely disables the request timeouts middleware from applying to this endpoint.
11+
/// </remarks>
12+
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
13+
public sealed class DisableRequestTimeoutAttribute : Attribute
14+
{
15+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Microsoft.AspNetCore.Http.Timeouts;
5+
6+
internal sealed class HttpRequestTimeoutFeature : IHttpRequestTimeoutFeature
7+
{
8+
private readonly CancellationTokenSource _timeoutCancellationTokenSource;
9+
10+
public HttpRequestTimeoutFeature(CancellationTokenSource timeoutCancellationTokenSource)
11+
{
12+
_timeoutCancellationTokenSource = timeoutCancellationTokenSource;
13+
}
14+
15+
public CancellationToken RequestTimeoutToken => _timeoutCancellationTokenSource.Token;
16+
17+
public void DisableTimeout()
18+
{
19+
_timeoutCancellationTokenSource.CancelAfter(Timeout.Infinite);
20+
}
21+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Microsoft.AspNetCore.Http.Timeouts;
5+
6+
internal interface ICancellationTokenLinker
7+
{
8+
(CancellationTokenSource linkedCts, CancellationTokenSource timeoutCts) GetLinkedCancellationTokenSource(HttpContext httpContext, CancellationToken originalToken, TimeSpan timeSpan);
9+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Microsoft.AspNetCore.Http.Timeouts;
5+
6+
/// <summary>
7+
///
8+
/// </summary>
9+
public interface IHttpRequestTimeoutFeature
10+
{
11+
/// <summary>
12+
///
13+
/// </summary>
14+
CancellationToken RequestTimeoutToken { get; }
15+
16+
/// <summary>
17+
///
18+
/// </summary>
19+
void DisableTimeout();
20+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Microsoft.Extensions.Logging;
5+
6+
namespace Microsoft.AspNetCore.Http.Timeouts;
7+
8+
internal static partial class LoggerExtensions
9+
{
10+
[LoggerMessage(1, LogLevel.Warning, "Timeout exception handled.")]
11+
public static partial void TimeoutExceptionHandled(this ILogger logger, Exception exception);
12+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Microsoft.AspNetCore.Http.Timeouts;
5+
6+
/// <summary>
7+
/// Metadata that provides endpoint-specific request timeouts.
8+
/// </summary>
9+
/// <remarks>
10+
/// The default policy will be ignored with this attribute applied.
11+
/// </remarks>
12+
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
13+
public sealed class RequestTimeoutAttribute : Attribute
14+
{
15+
/// <summary>
16+
/// The timeout to apply for this endpoint.
17+
/// </summary>
18+
public TimeSpan? Timeout { get; }
19+
20+
/// <summary>
21+
/// The name of the policy which needs to be applied.
22+
/// This value is case insensitve.
23+
/// </summary>
24+
public string? PolicyName { get; }
25+
26+
/// <summary>
27+
/// Creates a new instance of <see cref="RequestTimeoutAttribute"/> using the specified timeout.
28+
/// </summary>
29+
/// <param name="milliseconds">The amount of timeout for this specific endpoint.</param>
30+
public RequestTimeoutAttribute(int milliseconds)
31+
{
32+
Timeout = TimeSpan.FromMilliseconds(milliseconds);
33+
}
34+
35+
/// <summary>
36+
/// Creates a new instance of <see cref="RequestTimeoutAttribute"/> using the specified policy.
37+
/// </summary>
38+
/// <param name="policyName">The case-insensitve name of the policy which needs to be applied.</param>
39+
public RequestTimeoutAttribute(string policyName)
40+
{
41+
PolicyName = policyName;
42+
}
43+
}

0 commit comments

Comments
 (0)