Server
SimpleWServer is the main entry point to host a SimpleW Web server. You can use it in a procedural style (step-by-step calls) or in a fluent style (chaining calls).
Lifecycle actions
| Method | Behavior |
|---|---|
StartAsync(ct) | Starts listening without blocking (returns immediately). Validates options before starting. |
RunAsync(ct) | Starts the server and blocks until the server is stopped / cancelled. |
StopAsync() | Stops listening, closes sessions, disposes internal resources. |
ReloadListenerAsync(reconfigure, ct) | Hot-reloads the listener (endpoint / TLS, etc.) while the server is running. |
Instantiation
You can create a server from an IPAddress + port or from an EndPoint.
using System.Net;
using SimpleW;
var server1 = new SimpleWServer(IPAddress.Loopback, 8080);
var endpoint = new IPEndPoint(IPAddress.Any, 8081);
var server2 = new SimpleWServer(endpoint);Configuration
Configure(Action<SimpleWSServerOptions>) lets you customize server options before starting.
Important: calling
Configure(...)after the server has started throws an exception.
server.Configure(options => {
// Example: set options here (depends on SimpleWSServerOptions fields)
options.TcpNoDelay = true;
options.MaxRequestBodySize = 100 * 1024 * 1024; // 100Mo
});Options are automatically validated/normalized when starting (StartAsync calls Options.ValidateAndNormalize()).
INFO
See all options.
Optional: lifecycle callbacks (fluent)
You can hook into lifecycle events :
OnStarted(Action<SimpleWServer>)/OnStarted(Func<SimpleWServer, Task>)OnStopped(Action<SimpleWServer>)/OnStopped(Func<SimpleWServer, Task>)
var server = new SimpleWServer(IPAddress.Any, 8080)
.OnStarted(s => Console.WriteLine("Listening!"))
.OnStopped(s => Console.WriteLine("Stopped!"));
await server.RunAsync();Optional: replace the default Engine
By default, SimpleWServer uses SimpleWEngine as its network engine.
If you need a custom transport/listener implementation, you can replace it with UseEngine(...) before starting the server.
var server = new SimpleWServer(IPAddress.Any, 8080);
server.UseEngine(new MyCustomEngine());Your custom engine must implement ISimpleWEngine.
Typical use cases :
- Customize how incoming connections are accepted
- Experiment with another low-level listener implementation
- Extend the default engine behavior in a dedicated class
Optional: replace the default Router
By default, SimpleW uses the built-in Router implementation.
In advanced scenarios, you can replace the router with a custom implementation.
server.UseRouter(new MyCustomRouter());INFO
When the router is replaced, any registered route is lost.
Typical use cases :
- Implement a custom routing strategy
- Wrap the default router to add cross-cutting features
- Experiment with alternative routing pipelines
Optional: configure default Principal
By default, requests use an anonymous principal.
If you want the server to resolve a default principal for each request, you can use ConfigurePrincipalResolver(...).
server.ConfigurePrincipalResolver(session => {
if (session.Request.Headers.TryGetValue("X-User", out string? user) && !string.IsNullOrWhiteSpace(user)) {
return new HttpPrincipal(new HttpIdentity(
isAuthenticated: true,
authenticationType: "Header",
identifier: user,
name: user,
email: null,
roles: [ "user" ],
properties: null
));
}
return HttpPrincipal.Anonymous;
});This is useful for simple global identity resolution. For shared authentication and authorization rules, middleware is usually a better fit.
See the Principal guide.
Starting the server
StartAsync (non-blocking)
StartAsync() starts listening and returns immediately.
await server.StartAsync();
// app continues running...
Console.WriteLine("Server started!");This is useful if you already have your own host loop (service, worker, etc.) and you just want to start/stop the server as part of a bigger lifecycle.
RunAsync (blocking)
RunAsync() is a convenience method: it starts the server and then waits until the server is stopped or the provided token is cancelled.
using var cts = new CancellationTokenSource();
Console.CancelKeyPress += (_, e) => { e.Cancel = true; cts.Cancel(); };
await server.RunAsync(cts.Token);Stopping the server
StopAsync() triggers shutdown. Internally it :
- cancels the lifetime token
- closes the listen socket
- closes all sessions
- disposes timers and telemetry
- resets server state
await server.StopAsync();Calling StopAsync() multiple times is safe (it returns early if already stopped/stopping).
Reloading the listener at runtime
ReloadListenerAsync(...) lets you change listening settings while the server is running.
- The server must already be started, otherwise it throws.
- It temporarily stops accepting new connections (closes the listen socket), applies your
reconfigure(server)callback, then starts listening again. - Existing sessions are not the listener: reloading focuses on the accept loop + endpoint/TLS.
await server.ReloadListenerAsync(s => {
s.UseAddress(IPAddress.Any);
s.UsePort(9090);
// Optional: switch TLS config too
// s.UseHttps(newSslContext);
});If the reload fails, it will attempt a best-effort rollback to the previous endpoint/TLS and re-listen, then rethrow the exception.
Procedural vs Fluent usage
Procedural style
var server = new SimpleWServer(IPAddress.Any, 8080);
server.Configure(options => { /* ... */ });
server.MapGet("/", ctx => "Hello");
await server.RunAsync();Fluent style
Most configuration methods return SimpleWServer, so you can chain them :
await new SimpleWServer(IPAddress.Any, 8080)
.Configure(options => { /* ... */ })
.MapGet("/", ctx => "Hello")
// .UseHttps(sslContext)
.RunAsync();