Update for .NET 8.

This commit is contained in:
Pete Chown
2024-06-23 20:15:04 +01:00
parent c14d75b930
commit 3a70349e98
6 changed files with 39 additions and 20 deletions

4
.vscode/launch.json vendored
View File

@@ -10,7 +10,7 @@
"request": "launch", "request": "launch",
"preLaunchTask": "build", "preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path. // If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/bin/Debug/netcoreapp2.1/kestrel.dll", "program": "${workspaceFolder}/bin/Debug/net8.0/StandaloneKestrel.dll",
"args": [], "args": [],
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",
// For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window
@@ -25,4 +25,4 @@
"processId": "${command:pickProcess}" "processId": "${command:pickProcess}"
} }
,] ,]
} }

4
.vscode/tasks.json vendored
View File

@@ -7,9 +7,9 @@
"type": "process", "type": "process",
"args": [ "args": [
"build", "build",
"${workspaceFolder}/kestrel.csproj" "${workspaceFolder}/StandaloneKestrel.csproj"
], ],
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
} }
] ]
} }

View File

@@ -1,4 +1,5 @@
using System.Net.WebSockets; using System.Diagnostics.Metrics;
using System.Net.WebSockets;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Text; using System.Text;
using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Hosting.Server;
@@ -9,18 +10,12 @@ using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets;
using Microsoft.AspNetCore.WebSockets; using Microsoft.AspNetCore.WebSockets;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
namespace Kestrel; namespace Kestrel;
class Context class Context(IFeatureCollection features)
{ {
public IFeatureCollection features; public IFeatureCollection features = features;
public Context(IFeatureCollection features)
{
this.features = features;
}
} }
class Application : IHttpApplication<Context> class Application : IHttpApplication<Context>
@@ -58,16 +53,25 @@ class Application : IHttpApplication<Context>
await socket.SendAsync(new ArraySegment<byte>(message), WebSocketMessageType.Text, true, await socket.SendAsync(new ArraySegment<byte>(message), WebSocketMessageType.Text, true,
CancellationToken.None); CancellationToken.None);
await socket.ReceiveAsync(new byte[4096], CancellationToken.None); message = new byte[4096];
var received = await socket.ReceiveAsync(message, CancellationToken.None);
if (received.MessageType == WebSocketMessageType.Text)
Console.WriteLine($"Received: {Encoding.ASCII.GetString(message, 0, received.Count)}");
} }
else else
{ {
httpContext.Response.Headers.Add("Content-Type", new StringValues("text/plain")); httpContext.Response.Headers.ContentType = "text/plain";
await httpContext.Response.Body.WriteAsync(Encoding.ASCII.GetBytes("hello world\n")); await httpContext.Response.Body.WriteAsync(Encoding.ASCII.GetBytes("hello world\n"));
} }
} }
} }
class MeterFactory : IMeterFactory
{
public Meter Create(MeterOptions options) => new(options);
public void Dispose() { }
}
class AppServices : IServiceProvider class AppServices : IServiceProvider
{ {
public object? GetService(Type serviceType) public object? GetService(Type serviceType)
@@ -75,6 +79,11 @@ class AppServices : IServiceProvider
if (serviceType == typeof(ILoggerFactory)) if (serviceType == typeof(ILoggerFactory))
return new NullLoggerFactory(); return new NullLoggerFactory();
if (serviceType.FullName == "Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.KestrelMetrics")
#pragma warning disable IL2067
return Activator.CreateInstance(serviceType, new MeterFactory());
#pragma warning restore IL2067
return null; return null;
} }
} }

View File

@@ -12,10 +12,12 @@ dotnet publish -c Release
time ./time-startup time ./time-startup
``` ```
.NET 7 .NET 8
====== ======
I've been maintaining this package on and off since .NET Core 2.1. For most of this time, it has just been an interesting hack, but now it has a potential real-world use. .NET 7 supports [AOT compilation to a single executable](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/), but this is only for simple console applications, not for ASP.NET. Standalone Kestrel, though, works in this mode. You can build it like this: On .NET 7, the techniques used here were the only way to use Kestrel with native AOT compilation. With .NET 8, the [minimal APIs](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/overview?view=aspnetcore-8.0) are supported with native AOT. For most projects, this will be a better option. If you still want to use Kestrel directly, this project may be useful.
As with .NET 7, you can build an AOT version if that is useful:
``` ```
dotnet publish -r linux-x64 -c Release dotnet publish -r linux-x64 -c Release
@@ -23,7 +25,7 @@ dotnet publish -r linux-x64 -c Release
(This has only been tested on Linux. If you are not on Linux, you could try building an executable, but you will need to substitute a different runtime identifier in the above command.) (This has only been tested on Linux. If you are not on Linux, you could try building an executable, but you will need to substitute a different runtime identifier in the above command.)
You will see a few warnings during compilation, but as far as I can tell, the resulting executable works well. The executable is about 46M with debug information, 15M stripped. After the process has been invoked (using both HTTP GET and websockets) the resident memory is about 22M, far better than even the simplest ASP.NET application, and similar to Go. Unfortunately, with .NET 8, `UseHttps` has a dependency on the `KestrelMetrics` class, which is internal. Instances of this class are created by reflection, and there is a `TrimmerRootDescriptor` in the `.csproj` file to ensure that all the necessary code is included in the final executable. This is unsatisfactory because it depends on the internal design of Kestrel. Also, the `dotnet publish` step displays trim warnings for this class even though they are supposed to be turned off; presumably this is a bug in the .NET SDK. If you have a better solution for any of this, please let me know!
TLS TLS
=== ===

View File

@@ -1,10 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<PublishAot>true</PublishAot> <PublishAot>true</PublishAot>
</PropertyGroup> </PropertyGroup>
</Project> <ItemGroup>
<TrimmerRootDescriptor Include="TrimmerRoots.xml" />
</ItemGroup>
</Project>

5
TrimmerRoots.xml Normal file
View File

@@ -0,0 +1,5 @@
<linker>
<assembly fullname="Microsoft.AspNetCore.Server.Kestrel.Core">
<type fullname="Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.KestrelMetrics" preserve="all" />
</assembly>
</linker>