Skip to content

Commit 7c4a6ed

Browse files
committed
Add port sharing support to Minimal benchmarks on Linux
1 parent 40ab5f9 commit 7c4a6ed

File tree

1 file changed

+39
-2
lines changed

1 file changed

+39
-2
lines changed

src/BenchmarksApps/TechEmpower/Minimal/Program.cs

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
using Minimal;
66
using Minimal.Database;
77
using Minimal.Models;
8+
using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets;
9+
using System.Net.Sockets;
10+
using System.Net;
11+
using System.Runtime.InteropServices;
812

913
var builder = WebApplication.CreateBuilder(args);
1014

@@ -13,9 +17,41 @@
1317

1418
builder.WebHost.ConfigureKestrel(options =>
1519
{
16-
options.AllowSynchronousIO = true;
20+
options.AllowSynchronousIO = true;
1721
});
1822

23+
// Allow multiple processes bind to the same port. This also "works" on Windows in that it will
24+
// prevent address in use errors and hand off to another process if no others are available,
25+
// but it wouldn't round-robin new connections between processes like it will on Linux.
26+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
27+
{
28+
builder.WebHost.UseSockets(options =>
29+
{
30+
options.CreateBoundListenSocket = endpoint =>
31+
{
32+
if (endpoint is not IPEndPoint ip)
33+
{
34+
return SocketTransportOptions.CreateDefaultBoundListenSocket(endpoint);
35+
}
36+
37+
// Normally, we'd call CreateDefaultBoundListenSocket for the IPEndpoint too, but we need
38+
// to set ReuseAddress before calling bind, and CreateDefaultBoundListenSocket calls bind.
39+
var listenSocket = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
40+
41+
// Kestrel expects IPv6Any to bind to both IPv6 and IPv4
42+
if (ip.Address.Equals(IPAddress.IPv6Any))
43+
{
44+
listenSocket.DualMode = true;
45+
}
46+
47+
listenSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
48+
listenSocket.Bind(ip);
49+
50+
return listenSocket;
51+
};
52+
});
53+
}
54+
1955
// Load custom configuration
2056
var appSettings = new AppSettings();
2157
builder.Configuration.Bind(appSettings);
@@ -40,7 +76,8 @@
4076
var createFortunesTemplate = RazorSlice.ResolveSliceFactory<List<Fortune>>("/Templates/Fortunes.cshtml");
4177
var htmlEncoder = CreateHtmlEncoder();
4278

43-
app.MapGet("/fortunes", async (HttpContext context, Db db) => {
79+
app.MapGet("/fortunes", async (HttpContext context, Db db) =>
80+
{
4481
var fortunes = await db.LoadFortunesRows();
4582
var template = (RazorSliceHttpResult<List<Fortune>>)createFortunesTemplate(fortunes);
4683
template.HtmlEncoder = htmlEncoder;

0 commit comments

Comments
 (0)