Ultra lightweight highly customizable web server based on IHost and Socket classes. No third party dependencies.
$ dotnet add package WebHostWebHost 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)
Clone the repository and navigate to the project directory:
git clone <repository-url>
cd WebHost
Refer 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
WebHostBuilder
Middleware Pipeline
WebSocket and TLS Support
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.