|
5 | 5 | using Minimal;
|
6 | 6 | using Minimal.Database;
|
7 | 7 | 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; |
8 | 12 |
|
9 | 13 | var builder = WebApplication.CreateBuilder(args);
|
10 | 14 |
|
|
13 | 17 |
|
14 | 18 | builder.WebHost.ConfigureKestrel(options =>
|
15 | 19 | {
|
16 |
| - options.AllowSynchronousIO = true; |
| 20 | + options.AllowSynchronousIO = true; |
17 | 21 | });
|
18 | 22 |
|
| 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 | + |
19 | 55 | // Load custom configuration
|
20 | 56 | var appSettings = new AppSettings();
|
21 | 57 | builder.Configuration.Bind(appSettings);
|
|
40 | 76 | var createFortunesTemplate = RazorSlice.ResolveSliceFactory<List<Fortune>>("/Templates/Fortunes.cshtml");
|
41 | 77 | var htmlEncoder = CreateHtmlEncoder();
|
42 | 78 |
|
43 |
| -app.MapGet("/fortunes", async (HttpContext context, Db db) => { |
| 79 | +app.MapGet("/fortunes", async (HttpContext context, Db db) => |
| 80 | +{ |
44 | 81 | var fortunes = await db.LoadFortunesRows();
|
45 | 82 | var template = (RazorSliceHttpResult<List<Fortune>>)createFortunesTemplate(fortunes);
|
46 | 83 | template.HtmlEncoder = htmlEncoder;
|
|
0 commit comments