WebHost is an ultra-lightweight web server framework for .NET, designed to handle HTTP, WebSocket, and secure TLS/mTLS communication. It provides a modular and extensible architecture, integrating seamlessly with .NET's IHost for dependency injection and middleware configuration.
Provide fully customizable and low level access to http request. This package was born from the need to run a .NET C# web server on any .NET supported platform, which is not possible using ASP.NET Core.
No third party libraries used, the only dependencies are
Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.0)
Microsoft.Extensions.Hosting (>= 9.0.0)
- Flexible Hosting: Supports both TLS and non-TLS connections.
- WebSocket Support: Implements WebSocket communication in compliance with RFC 6455.
- Middleware Pipeline: Fully configurable request handling pipeline.
- Route Mapping: Dynamically registers route handlers with attributes or fluent API.
- Extensible Architecture: Integrates with .NET's dependency injection for custom services and middleware.
- Lightweight Design: Minimal overhead with high-performance socket-based networking.
- HTTP/1.x: Yes
- HTTP/2.0: In progress
- HTTP/3.0: Not planned yet
- Yes
- RFC 6455 protocol
- Missing fragmented frames support (to be added)
- .NET SDK (version 8.0 or later recommended)
Clone the repository and navigate to the project directory:
git clone <repository-url>
cd WebHostRefer to Examples folder for detailed usage examples!
var host = WebHostApp.CreateBuilder()
.SetEndpoint("127.0.0.1", 9001)
.UseTls(options =>
{
options.ServerCertificate = LoadCertificate();
})
.MapGet("/route", sp => async context =>
{
// Access http request params
//
Console.WriteLine($"Received HttpMethod: {context.Request.HttpMethod}");
Console.WriteLine($"Received query params: {context.Request.QueryParameters}");
foreach (var header in context.Request.Headers)
Console.WriteLine($"Received header: {header}");
Console.WriteLine($"Received body: {context.Request.Body}");
// Respond
//
var exampleClass = new
{
Name = "John",
Address = "World"
};
var jsonString = JsonSerializer.Serialize(exampleClass);
context.Response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(jsonString, Encoding.UTF8, "application/json"),
};
context.Response.Headers.ConnectionClose = false;
context.Response.Content.Headers.ContentLength = Encoding.UTF8.GetByteCount(jsonString);
await context.SendAsync(await context.Response.ToBytes());
})
.Build();
await host.RunAsync();
static X509Certificate2 LoadCertificate() {
// Load your TLS certificate here
}// Basic global error handling middleware example
//
builder.UseMiddleware(scope => async (context, next) =>
{
var logger = scope.GetRequiredService<ILogger<Program>>();
logger.LogDebug("Executing..");
// Wrap the endpoint in a try catch for global error handling
//
try
{
await next(context);
}
// In case a ServiceException type was caught, the status code is known to be used on the http response
//
catch (ServiceException serviceEx)
{
logger.LogError("ServiceException was caught and being handled:{Message}", serviceEx.Message);
var message = serviceEx.Message;
context.Response = new HttpResponseMessage((HttpStatusCode)serviceEx.StatusCode)
{
Content = new StringContent(message, Encoding.UTF8, "text/pain"),
};
context.Response.Headers.ConnectionClose = false;
context.Response.Content.Headers.ContentLength = Encoding.UTF8.GetByteCount(message);
await context.SendAsync(await context.Response.ToBytes());
}
// In case a regular exception is caught, assume the http response status code to be 500
//
catch (Exception ex)
{
logger.LogError("Exception was caught and being handled:{Message}", ex.Message);
var message = ex.Message;
context.Response = new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(message, Encoding.UTF8, "text/pain"),
};
context.Response.Headers.ConnectionClose = false;
context.Response.Content.Headers.ContentLength = Encoding.UTF8.GetByteCount(message);
await context.SendAsync(await context.Response.ToBytes());
}
});.MapGet("/websocket", scope => async (context) =>
{
var arrayPool = ArrayPool<byte>.Shared;
var buffer = arrayPool.Rent(10000000);
while (true)
{
var receivedData = await context.WsReadAsync(buffer);
if (receivedData.Item2 == WsFrameType.Close)
break;
if (receivedData.Item1.IsEmpty)
break;
await context.WsSendAsync(receivedData.Item1, 0x01);
}
arrayPool.Return(buffer);
});-
WebHostApp- The main entry point for configuring and starting the server.
- Supports fluent API for configuration.
-
WebHostBuilder- Provides methods to configure endpoints, TLS settings, and middleware.
-
Middleware Pipeline
- Processes requests through a dynamic pipeline of middleware components.
-
WebSocket and TLS Support
- Handles WebSocket handshakes and messages.
- Manages secure communication with TLS/mTLS.
Contributions are welcome! Feel free to open issues or submit pull requests to improve the project.
This project is licensed under the MIT License. See the LICENSE file for details.
- Built with .NET and inspired by high-performance web hosting frameworks.