Skip to content

Commit 33fdf54

Browse files
authored
feat: tls jobs comparing http.sys win vs kestrel linux (#2037)
1 parent 272ecd8 commit 33fdf54

18 files changed

+519
-0
lines changed

build/job-variables.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ variables:
6565
value: --config https://raw.githubusercontent.com/aspnet/Benchmarks/main/scenarios/json.benchmarks.yml
6666
- name: antiforgeryJobs
6767
value: --config https://raw.githubusercontent.com/aspnet/Benchmarks/main/scenarios/antiforgery.benchmarks.yml
68+
- name: tlsJobs
69+
value: --config https://raw.githubusercontent.com/aspnet/Benchmarks/main/scenarios/tls.benchmarks.yml
6870
- name: goldilocksJobs
6971
value: --config https://raw.githubusercontent.com/aspnet/Benchmarks/main/scenarios/goldilocks.benchmarks.yml
7072
- name: monoJobs

build/trend-scenarios.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,23 @@ parameters:
100100
- displayName: Antiforgery Token Generation
101101
arguments: --scenario antiforgery-generation $(antiforgeryJobs) --property scenario=AntiforgeryTokenGeneration
102102

103+
# TLS
104+
105+
- displayName: "HttpSys Windows: TLS Handshakes"
106+
arguments: --scenario tls-handshakes-httpsys $(tlsJobs) --property scenario=HttpSysTLSHandshakes --application.options.requiredOperatingSystem windows
107+
108+
- displayName: "HttpSys Windows: mTLS Handshakes"
109+
arguments: --scenario mTls-handshakes-httpsys $(tlsJobs) --property scenario=HttpSysMutualTLSHandshakes --application.options.requiredOperatingSystem windows
110+
111+
- displayName: "Kestrel Linux: TLS Handshakes"
112+
arguments: --scenario tls-handshakes-kestrel $(tlsJobs) --property scenario=KestrelTLSHandshakes --application.options.requiredOperatingSystem linux
113+
114+
- displayName: "Kestrel Linux: mTLS Handshakes"
115+
arguments: --scenario mTls-handshakes-kestrel $(tlsJobs) --property scenario=KestrelMutualTLSHandshakes --application.options.requiredOperatingSystem linux
116+
117+
- displayName: "Kestrel Linux: TLS Renegotiation"
118+
arguments: --scenario tls-renegotiation-kestrel $(tlsJobs) --property scenario=KestrelTLSRenegotiation --application.options.requiredOperatingSystem linux
119+
103120
steps:
104121
- ${{ each s in parameters.scenarios }}:
105122
- task: PublishToAzureServiceBus@2

scenarios/tls.benchmarks.yml

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
imports:
2+
- https://raw.githubusercontent.com/dotnet/crank/main/src/Microsoft.Crank.Jobs.Wrk/wrk.yml
3+
- https://raw.githubusercontent.com/dotnet/crank/main/src/Microsoft.Crank.Jobs.Bombardier/bombardier.yml
4+
- https://raw.githubusercontent.com/dotnet/crank/main/src/Microsoft.Crank.Jobs.HttpClient/httpclient.yml
5+
- https://github.com/aspnet/Benchmarks/blob/main/scenarios/aspnet.profiles.yml?raw=true
6+
7+
variables:
8+
serverPort: 5000
9+
10+
jobs:
11+
httpSysServer:
12+
source:
13+
repository: https://github.com/aspnet/benchmarks.git
14+
branchOrCommit: main
15+
project: src/BenchmarksApps/TLS/HttpSys/HttpSys.csproj
16+
readyStateText: Application started.
17+
variables:
18+
mTLS: false
19+
certValidationConsoleEnabled: false
20+
statsEnabled: false
21+
arguments: "--urls https://{{serverAddress}}:{{serverPort}} --mTLS {{mTLS}} --certValidationConsoleEnabled {{certValidationConsoleEnabled}} --statsEnabled {{statsEnabled}}"
22+
23+
kestrelServer:
24+
source:
25+
repository: https://github.com/aspnet/benchmarks.git
26+
branchOrCommit: main
27+
project: src/BenchmarksApps/TLS/Kestrel/Kestrel.csproj
28+
readyStateText: Application started.
29+
variables:
30+
mTLS: false
31+
tlsRenegotiation: false
32+
certValidationConsoleEnabled: false
33+
statsEnabled: false
34+
arguments: "--urls https://{{serverAddress}}:{{serverPort}} --mTLS {{mTLS}} --certValidationConsoleEnabled {{certValidationConsoleEnabled}} --statsEnabled {{statsEnabled}} --tlsRenegotiation {{tlsRenegotiation}}"
35+
36+
scenarios:
37+
38+
# HTTP.SYS
39+
40+
tls-handshakes-httpsys:
41+
application:
42+
job: httpSysServer
43+
load:
44+
job: wrk
45+
variables:
46+
path: /hello-world
47+
presetHeaders: connectionclose
48+
connections: 32
49+
serverScheme: https
50+
51+
mTls-handshakes-httpsys:
52+
application:
53+
job: httpSysServer
54+
variables:
55+
mTLS: true
56+
certValidationConsoleEnabled: false # only for debug purposes
57+
load:
58+
job: httpclient
59+
variables:
60+
path: /hello-world
61+
presetHeaders: connectionclose
62+
connections: 32
63+
serverScheme: https
64+
certPath: https://raw.githubusercontent.com/aspnet/Benchmarks/refs/heads/main/src/BenchmarksApps/TLS/HttpSys/testCert.pfx
65+
certPwd: testPassword
66+
67+
# Kestrel
68+
69+
tls-handshakes-kestrel:
70+
application:
71+
job: kestrelServer
72+
load:
73+
job: wrk
74+
variables:
75+
path: /hello-world
76+
presetHeaders: connectionclose
77+
connections: 32
78+
serverScheme: https
79+
80+
mTls-handshakes-kestrel:
81+
application:
82+
job: kestrelServer
83+
variables:
84+
mTLS: true
85+
certValidationConsoleEnabled: false # only for debug purposes
86+
load:
87+
job: httpclient
88+
variables:
89+
path: /hello-world
90+
presetHeaders: connectionclose
91+
connections: 32
92+
serverScheme: https
93+
certPath: https://raw.githubusercontent.com/aspnet/Benchmarks/refs/heads/main/src/BenchmarksApps/TLS/Kestrel/testCert.pfx
94+
certPwd: testPassword
95+
96+
tls-renegotiation-kestrel:
97+
application:
98+
job: kestrelServer
99+
variables:
100+
mTLS: false
101+
tlsRenegotiation: true
102+
certValidationConsoleEnabled: false # only for debug purposes
103+
load:
104+
job: httpclient
105+
variables:
106+
path: /hello-world
107+
presetHeaders: connectionclose
108+
connections: 32
109+
serverScheme: https
110+
certPath: https://raw.githubusercontent.com/aspnet/Benchmarks/refs/heads/main/src/BenchmarksApps/TLS/Kestrel/testCert.pfx
111+
certPwd: testPassword

src/BenchmarksApps.sln

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Json", "BenchmarksApps\Json
6666
EndProject
6767
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Antiforgery", "BenchmarksApps\Antiforgery\Antiforgery.csproj", "{38AEFB02-3AE4-4E01-8FF9-FF09797C5853}"
6868
EndProject
69+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TLS", "TLS", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
70+
EndProject
71+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpSys", "BenchmarksApps\TLS\HttpSys\HttpSys.csproj", "{455942DF-6C8E-4054-AF1D-41A10BE1466F}"
72+
EndProject
73+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kestrel", "BenchmarksApps\TLS\Kestrel\Kestrel.csproj", "{291DCDF7-4B7C-D687-A62B-9DF7DF50F2F2}"
74+
EndProject
6975
Global
7076
GlobalSection(SolutionConfigurationPlatforms) = preSolution
7177
Debug_Database|Any CPU = Debug_Database|Any CPU
@@ -258,6 +264,22 @@ Global
258264
{38AEFB02-3AE4-4E01-8FF9-FF09797C5853}.Release_Database|Any CPU.Build.0 = Release_Database|Any CPU
259265
{38AEFB02-3AE4-4E01-8FF9-FF09797C5853}.Release|Any CPU.ActiveCfg = Release|Any CPU
260266
{38AEFB02-3AE4-4E01-8FF9-FF09797C5853}.Release|Any CPU.Build.0 = Release|Any CPU
267+
{455942DF-6C8E-4054-AF1D-41A10BE1466F}.Debug_Database|Any CPU.ActiveCfg = Debug_Database|Any CPU
268+
{455942DF-6C8E-4054-AF1D-41A10BE1466F}.Debug_Database|Any CPU.Build.0 = Debug_Database|Any CPU
269+
{455942DF-6C8E-4054-AF1D-41A10BE1466F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
270+
{455942DF-6C8E-4054-AF1D-41A10BE1466F}.Debug|Any CPU.Build.0 = Debug|Any CPU
271+
{455942DF-6C8E-4054-AF1D-41A10BE1466F}.Release_Database|Any CPU.ActiveCfg = Release_Database|Any CPU
272+
{455942DF-6C8E-4054-AF1D-41A10BE1466F}.Release_Database|Any CPU.Build.0 = Release_Database|Any CPU
273+
{455942DF-6C8E-4054-AF1D-41A10BE1466F}.Release|Any CPU.ActiveCfg = Release|Any CPU
274+
{455942DF-6C8E-4054-AF1D-41A10BE1466F}.Release|Any CPU.Build.0 = Release|Any CPU
275+
{291DCDF7-4B7C-D687-A62B-9DF7DF50F2F2}.Debug_Database|Any CPU.ActiveCfg = Debug_Database|Any CPU
276+
{291DCDF7-4B7C-D687-A62B-9DF7DF50F2F2}.Debug_Database|Any CPU.Build.0 = Debug_Database|Any CPU
277+
{291DCDF7-4B7C-D687-A62B-9DF7DF50F2F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
278+
{291DCDF7-4B7C-D687-A62B-9DF7DF50F2F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
279+
{291DCDF7-4B7C-D687-A62B-9DF7DF50F2F2}.Release_Database|Any CPU.ActiveCfg = Release_Database|Any CPU
280+
{291DCDF7-4B7C-D687-A62B-9DF7DF50F2F2}.Release_Database|Any CPU.Build.0 = Release_Database|Any CPU
281+
{291DCDF7-4B7C-D687-A62B-9DF7DF50F2F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
282+
{291DCDF7-4B7C-D687-A62B-9DF7DF50F2F2}.Release|Any CPU.Build.0 = Release|Any CPU
261283
EndGlobalSection
262284
GlobalSection(SolutionProperties) = preSolution
263285
HideSolutionNode = FALSE
@@ -273,6 +295,8 @@ Global
273295
{3D2573DE-CE7A-4CB8-A980-8C8636EE059E} = {398A40DA-FE1D-4B4D-A580-A33E29885553}
274296
{31B61CD7-4CF6-464F-B418-04C700A17CB9} = {6A69DE6C-07A6-4ABE-A4D2-0F983A33BBF8}
275297
{D6616E03-A2DA-4929-AD28-595ECC4C004D} = {B6DB234C-8F80-4160-B95D-D70AFC444A3D}
298+
{455942DF-6C8E-4054-AF1D-41A10BE1466F} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
299+
{291DCDF7-4B7C-D687-A62B-9DF7DF50F2F2} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
276300
EndGlobalSection
277301
GlobalSection(ExtensibilityGlobals) = postSolution
278302
SolutionGuid = {117072DC-DE12-4F74-90CA-692FA2BE8DCB}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net9.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
</PropertyGroup>
8+
9+
</Project>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@HttpSys_HostAddress_Http = http://localhost:5001
2+
@HttpSys_HostAddress_Https = https://localhost:5000
3+
4+
GET {{HttpSys_HostAddress_Https}}/hello-world
5+
6+
###
7+
8+
GET {{HttpSys_HostAddress_Http}}/hello-world
9+
10+
###
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
using Microsoft.AspNetCore.Mvc;
2+
using Microsoft.AspNetCore.Server.HttpSys;
3+
4+
var builder = WebApplication.CreateBuilder(args);
5+
builder.Logging.ClearProviders();
6+
7+
var writeCertValidationEventsToConsole = bool.TryParse(builder.Configuration["certValidationConsoleEnabled"], out var certValidationConsoleEnabled) && certValidationConsoleEnabled;
8+
var statsEnabled = bool.TryParse(builder.Configuration["statsEnabled"], out var connectionStatsEnabledConfig) && connectionStatsEnabledConfig;
9+
var mTlsEnabled = bool.TryParse(builder.Configuration["mTLS"], out var mTlsEnabledConfig) && mTlsEnabledConfig;
10+
var listeningEndpoints = builder.Configuration["urls"] ?? "https://localhost:5000/";
11+
12+
#pragma warning disable CA1416 // Can be launched only on Windows (HttpSys)
13+
builder.WebHost.UseHttpSys(options =>
14+
{
15+
// meaning client can send a certificate, but it can be explicitly requested by server as well (renegotiation)
16+
options.ClientCertificateMethod = ClientCertificateMethod.AllowRenegotation;
17+
});
18+
#pragma warning restore CA1416 // Can be launched only on Windows (HttpSys)
19+
20+
var app = builder.Build();
21+
22+
app.MapGet("/hello-world", () =>
23+
{
24+
return Results.Ok("Hello World!");
25+
});
26+
27+
var connectionIds = new HashSet<string>();
28+
var fetchedCertsCounter = 0;
29+
30+
if (statsEnabled)
31+
{
32+
Console.WriteLine("Registered stats middleware");
33+
app.Use(async (context, next) =>
34+
{
35+
connectionIds.Add(context.Connection.Id);
36+
Console.WriteLine($"[stats] unique connections established: {connectionIds.Count}; fetched certificates: {fetchedCertsCounter}");
37+
38+
await next();
39+
});
40+
}
41+
42+
if (mTlsEnabled)
43+
{
44+
// this is an http.sys middleware to get a cert
45+
Console.WriteLine("Registered client cert validation middleware");
46+
47+
app.Use(async (context, next) => {
48+
var clientCert = context.Connection.ClientCertificate;
49+
if (clientCert is null)
50+
{
51+
if (writeCertValidationEventsToConsole)
52+
{
53+
Console.WriteLine($"No client certificate provided. Fetching for connection {context.Connection.Id}");
54+
}
55+
56+
clientCert = await context.Connection.GetClientCertificateAsync(CancellationToken.None);
57+
Interlocked.Increment(ref fetchedCertsCounter);
58+
}
59+
else
60+
{
61+
if (writeCertValidationEventsToConsole)
62+
{
63+
Console.WriteLine($"client certificate ({clientCert.Thumbprint}) already exists on the connection {context.Connection.Id}");
64+
}
65+
}
66+
67+
// we have a client cert here, and lets imagine we do the validation here
68+
// if (clientCert.Thumbprint != "1234567890") throw new NotImplementedException();
69+
70+
await next();
71+
});
72+
}
73+
74+
await app.StartAsync();
75+
Console.WriteLine("Application Info:");
76+
if (mTlsEnabled)
77+
{
78+
Console.WriteLine($"\tmTLS is enabled (client cert is required)");
79+
}
80+
if (writeCertValidationEventsToConsole)
81+
{
82+
Console.WriteLine($"\tenabled logging certificate validation events to console");
83+
}
84+
if (statsEnabled)
85+
{
86+
Console.WriteLine($"\tenabled logging stats to console");
87+
}
88+
Console.WriteLine($"\tlistening endpoints: {listeningEndpoints}");
89+
Console.WriteLine("--------------------------------");
90+
91+
Console.WriteLine("Application started.");
92+
await app.WaitForShutdownAsync();
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"$schema": "http://json.schemastore.org/launchsettings.json",
3+
"profiles": {
4+
"https": {
5+
"commandName": "Project",
6+
"dotnetRunMessages": true,
7+
"launchBrowser": true,
8+
"launchUrl": "hello-world",
9+
"applicationUrl": "https://localhost:5000;http://localhost:5001",
10+
"environmentVariables": {
11+
"ASPNETCORE_ENVIRONMENT": "Development"
12+
}
13+
}
14+
}
15+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning"
6+
}
7+
},
8+
"mTLS": "true",
9+
"certValidationConsoleEnabled": "true"
10+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning"
6+
}
7+
},
8+
"AllowedHosts": "*"
9+
}

0 commit comments

Comments
 (0)