CosmoApiServer.Core
2.0.1
dotnet add package CosmoApiServer.Core --version 2.0.1
NuGet\Install-Package CosmoApiServer.Core -Version 2.0.1
<PackageReference Include="CosmoApiServer.Core" Version="2.0.1" />
<PackageVersion Include="CosmoApiServer.Core" Version="2.0.1" />
<PackageReference Include="CosmoApiServer.Core" />
paket add CosmoApiServer.Core --version 2.0.1
#r "nuget: CosmoApiServer.Core, 2.0.1"
#:package CosmoApiServer.Core@2.0.1
#addin nuget:?package=CosmoApiServer.Core&version=2.0.1
#tool nuget:?package=CosmoApiServer.Core&version=2.0.1
CosmoApiServer
A high-performance, zero-dependency HTTP server framework for .NET 10, built entirely on System.IO.Pipelines and System.Net.Sockets.
No DotNetty. No ASP.NET. No Kestrel. Just raw sockets → pipes → your handlers.
Benchmark
Sequential · 1,000 rounds · keep-alive · macOS arm64
| Scenario | CosmoApiServer | ASP.NET Core | P50 (Cosmo) | Advantage |
|---|---|---|---|---|
| GET /ping | 13,423 ops/s | 10,695 ops/s | 0.07 ms | +26% |
| GET /json | 13,158 ops/s | 10,091 ops/s | 0.08 ms | +30% |
| GET /route/{id} | 15,083 ops/s | 10,225 ops/s | 0.07 ms | +48% |
| POST /echo | 14,045 ops/s | 10,000 ops/s | 0.07 ms | +40% |
| GET /large-json (1000 items) | 2,312 ops/s | 1,897 ops/s | 0.43 ms | +22% |
| GET /query | 14,771 ops/s | 11,161 ops/s | 0.07 ms | +32% |
| POST /form | 13,986 ops/s | 9,551 ops/s | 0.07 ms | +46% |
| GET /headers | 13,106 ops/s | 10,395 ops/s | 0.08 ms | +26% |
| GET /stream (NDJSON, 10 items) | 8,224 ops/s | 9,443 ops/s | 0.12 ms | −13% |
| GET /file (64 KB) | 7,424 ops/s | 5,519 ops/s | 0.13 ms | +35% |
9 of 10 scenarios win. The /stream gap is chunked-encoding overhead vs Kestrel's tuned chunked encoder.
Razor Component Rendering (100-row table)
| Framework | Throughput | P50 Latency | Advantage |
|---|---|---|---|
| CosmoApiServer | 4,235 ops/sec | 0.24 ms | +141% |
| Blazor SSR (Static) | 1,754 ops/sec | 0.57 ms | Baseline |
Why so fast?
Traditional .NET HTTP servers (including Kestrel and DotNetty-based servers) have at least one thread-pool context switch per request. CosmoApiServer eliminates this:
Socket → PipeWriter → PipeReader → Parser → Middleware → PipeWriter → Socket
Everything runs inline on the connection task. No EventLoop→ThreadPool hand-off. No intermediate byte arrays. No string allocation on the hot path.
Key design decisions:
- Zero-copy parsing —
Http11ParserusesReadOnlySpan<byte>andSequenceReader<byte>directly over the pipe buffer. - Zero-allocation Headers — Headers are stored as
ReadOnlyMemory<byte>and only materialized to strings if accessed. - Lazy DI scope —
LazyScopeProvideronly callsIServiceProvider.CreateScope()if a service is actually resolved. - Object Pooling —
HttpContext,HttpRequest, andHttpResponseare pooled and reused to eliminate GC pressure. - Async State Machine Rendering —
RenderTreeBuilderuses a struct-based command buffer for non-blocking, high-speed SSR. - Span-based routing —
RouteTableuses aConcurrentDictionarycache and span-based matching for O(1) lookups.
Features
- HTTP/1.1 keep-alive (pipelined)
- HTTP/2 (h2c cleartext + ALPN over TLS)
- TLS via
SslStreamwith ALPN (h2/http/1.1) - Razor Components — Full
.razorsupport with@page,[Parameter], andCascadingParameters - Routable Components — Components can define their own routes via
@pagewithout a controller - Form Components —
<EditForm>,<InputText>,<InputNumber>,<InputSelect>,<InputCheckbox>,<InputTextArea> - Change Detection — Snapshot-based dirty tracking with
EditContext,FieldIdentifier, andFieldState @injectDependency Injection — Inject services into Razor components via@injectEventCallback/EventCallback<T>— Parent-child component communication<CascadingValue>— Provide values to the entire descendant component treeNavigationManager— Programmatic navigation and URI utilities@bindSupport — Two-way data binding viaBindConverter- Validation —
<ValidationMessage>,<ValidationSummary>, per-field CSS classes (modified,valid,invalid) - Attribute-based controllers (
[HttpGet],[HttpPost],[Route],[Authorize]) - Convention-based routing (
MapGet,MapPost, …) - JSON request/response (
WriteJson,ReadJson<T>) IAsyncEnumerable<T>→ NDJSON streaming response- Middleware pipeline (
UseLogging,UseCors,UseJwtAuthentication, customIMiddleware) - WebSockets (
HttpContext.AcceptWebSocketAsync()) - OpenAPI & Swagger UI auto-generation
- Security Middlewares (CSRF, HSTS, HTTPS Redirection, CSRF Validation)
- Model Validation via DataAnnotations (Controllers & Components)
- Zero-Copy File Serving —
HttpResponse.SendFileAsync()streams directly from disk to socket
Razor Components
CosmoApiServer includes a first-class implementation of Razor Components (similar to Blazor SSR). This provides the power of Razor syntax (C# + HTML) with the performance of a zero-dependency framework.
Why Razor Components?
- High Performance: Renders directly to
CosmoApiServer'sHttpResponsebuffers using an optimized async state machine. - Routable: Use
@page "/my-route"directly in your.razorfiles. - Cascading Parameters: Share state (like
ModelStateorUser) down the component tree automatically. - Validation: Support for
DataAnnotationswith built-in<ValidationMessage>and<ValidationSummary />. - Form Components:
<EditForm>,<InputText>,<InputNumber>,<InputCheckbox>,<InputSelect>,<InputTextArea>. - Change Detection: Snapshot-based dirty tracking via
EditContext— knows exactly which fields changed and can revert. - Dependency Injection:
@injectresolves services from the DI container into components. - EventCallback: Type-safe parent↔child component communication.
Usage
Create a .razor file in your project:
@page "/hello/{Name}"
@inherits Microsoft.AspNetCore.Components.ComponentBase
<h1>Hello, @Name!</h1>
<div class="alert alert-success">
Current Time: @DateTime.Now
</div>
@code {
[Parameter] public string Name { get; set; }
}
Enable in Program.cs:
var builder = CosmoWebApplicationBuilder.Create()
.AddRazorComponents(); // Scans for @page components
Forms & Change Detection
CosmoApiServer provides a complete form system with automatic change tracking — no INotifyPropertyChanged required.
EditForm with Input Components
@page "/contact"
@inherits Microsoft.AspNetCore.Components.ComponentBase
<EditForm Model="@person" Action="/contact" Method="post" CssClass="form-group">
<InputText Name="name" Value="@person.Name" Placeholder="Full name" Required="true" />
<InputNumber Name="age" Value="@person.Age" Min="0" Max="150" />
<InputCheckbox Name="subscribe" Value="@person.Subscribe" />
<InputTextArea Name="bio" Value="@person.Bio" Rows="5" Placeholder="Tell us about yourself" />
<InputSelect Name="country" Value="@person.Country">
<option value="us">United States</option>
<option value="uk">United Kingdom</option>
</InputSelect>
<ValidationMessage For="Name" />
<ValidationSummary />
<button type="submit">Submit</button>
</EditForm>
@code {
private PersonModel person = new() { Name = "Alice", Age = 30 };
public class PersonModel
{
[Required] public string? Name { get; set; }
[Range(0, 150)] public int Age { get; set; }
public bool Subscribe { get; set; }
public string? Bio { get; set; }
public string? Country { get; set; }
}
}
Change Detection with EditContext
EditContext takes a snapshot of your model at creation time and detects changes by comparing current property values against the original snapshot. This means:
- No dirty flags — actual value comparison, not "was touched"
- Revert-aware — changing
"Alice"→"Bob"→"Alice"is correctly detected as unmodified - Zero boilerplate — works with any POCO model via reflection
var model = new PersonModel { Name = "Alice", Age = 30 };
var ctx = new EditContext(model);
// Initially clean
ctx.IsModified(); // false
// Change a field
model.Name = "Bob";
ctx.NotifyFieldChanged("Name");
// Query state
ctx.IsModified(); // true
ctx.IsModified("Name"); // true
ctx.IsModified("Age"); // false
ctx.GetModifiedFields(); // ["Name"]
// Per-field state
var state = ctx.GetFieldState("Name");
state.IsModified; // true
state.OriginalValue; // "Alice"
state.CurrentValue; // "Bob"
// CSS classes for styling (combines: "modified", "valid", "invalid")
ctx.FieldCssClass("Name"); // "modified valid"
// Reset
ctx.MarkAsUnmodified(); // retakes snapshot, all fields clean
ctx.IsModified(); // false
Events
var ctx = new EditContext(model);
ctx.OnFieldChanged += (fieldIdentifier) =>
{
Console.WriteLine($"{fieldIdentifier.FieldName} changed");
};
ctx.OnValidationRequested += () =>
{
Console.WriteLine("Validation was triggered");
};
ctx.OnValidationStateChanged += () =>
{
Console.WriteLine("Validation results updated");
};
Field CSS Classes
The FieldCssClassProvider automatically generates CSS classes based on field state:
| State | CSS Class | When |
|---|---|---|
| Modified | modified |
Field value differs from snapshot |
| Valid | valid |
Field passed validation |
| Invalid | invalid |
Field has a validation error |
Classes combine: a modified field that failed validation gets "modified invalid".
Override with a custom provider:
public class BootstrapCssProvider : FieldCssClassProvider
{
public override string GetFieldCssClass(EditContext editContext, FieldIdentifier fieldIdentifier)
{
var state = editContext.GetFieldState(fieldIdentifier);
if (state is null) return string.Empty;
if (state.IsInvalid) return "is-invalid";
if (state.IsModified && state.IsValid) return "is-valid";
return string.Empty;
}
}
ctx.CssClassProvider = new BootstrapCssProvider();
Dependency Injection in Components
Use @inject to resolve services from the DI container:
@page "/dashboard"
@inherits Microsoft.AspNetCore.Components.ComponentBase
@inject NavigationManager Nav
@inject ILogger<Dashboard> Logger
<h1>Dashboard</h1>
<p>Current URL: @Nav.Uri</p>
<a href="@Nav.ToAbsoluteUri("/settings")">Settings</a>
@code {
protected override void OnInitialized()
{
Logger.LogInformation("Dashboard loaded at {Path}", Nav.Path);
}
}
NavigationManager
An injectable service for programmatic navigation and URI utilities:
// Registered automatically — just @inject it
@inject NavigationManager Nav
// Properties
Nav.Uri // "http://localhost:8080/products?page=2"
Nav.Path // "/products"
Nav.QueryString // "?page=2"
Nav.BaseUri // "http://localhost:8080/"
// Conversion
Nav.ToAbsoluteUri("/foo/bar"); // "http://localhost:8080/foo/bar"
Nav.ToBaseRelativePath("http://localhost:8080/foo/bar"); // "foo/bar"
// Navigation (sets Location header + 302)
Nav.NavigateTo("/login");
Nav.NavigateTo("/login", forceLoad: true);
EventCallback
Parent-child component communication using EventCallback and EventCallback<T>:
<ChildComponent OnClick="HandleClick" OnSelect="HandleSelect" />
<p>Clicked: @clicked</p>
<p>Selected: @selected</p>
@code {
private bool clicked;
private string? selected;
private void HandleClick() => clicked = true;
private void HandleSelect(string item) => selected = item;
}
<button @onclick="OnClick">Click me</button>
<button @onclick="() => OnSelect.InvokeAsync("Item A")">Select A</button>
@code {
[Parameter] public EventCallback OnClick { get; set; }
[Parameter] public EventCallback<string> OnSelect { get; set; }
}
Programmatic usage:
// Simple callback
var cb = new EventCallback(() => Console.WriteLine("Clicked!"));
await cb.InvokeAsync();
// Typed callback
var cb = new EventCallback<string>(value => Console.WriteLine($"Got: {value}"));
await cb.InvokeAsync("hello");
// Factory (used by Razor-generated code)
var cb = EventCallbackFactory.Create(this, () => { /* handler */ });
CascadingValue
Share values with all descendant components without explicit parameter passing:
<CascadingValue Value="@theme">
<MainLayout />
</CascadingValue>
@code {
private ThemeInfo theme = new() { PrimaryColor = "#007bff" };
}
@code {
[CascadingParameter] public ThemeInfo? Theme { get; set; }
}
Projects in this repository
| Project | Description |
|---|---|
src/CosmoApiServer.Core |
Core framework library |
samples/BlazorSqlSample |
Replicated Blazor structure with SQL streaming and components |
samples/WeatherApp |
Full REST API: JWT auth, DI, streaming, CosmoSQLClient |
templates/CosmoRazorServerTemplate |
dotnet new cosmorazor template |
tests/CosmoApiServer.Core.Tests |
108 unit tests for routing, middleware, components, forms, change detection |
Changelog
v2.0.1
- Streaming performance —
ChunkedBodyStreamnow stages multipleWriteAsynccalls into a single chunk perFlushAsync, eliminating one chunk-header per write. NDJSON streaming throughput improved from ~4,300 to ~8,200 ops/s. - Fixed
Flush()blocking — SyncFlush()on stream writers was calling.GetAwaiter().GetResult()on an async pipe flush, risking thread-pool starvation. Now a no-op; callers must useFlushAsync. - Fixed duplicate response headers —
WriteStreamingResponseAsyncdid not set_headersWritten, causingEnsureHeadersWritten()to append a second set of HTTP headers after the body. - Fixed connection lifecycle —
Http11Connectionnow shares aCancellationTokenSourcebetweenFillPipeAsyncandProcessAsync. When either side finishes, the other is cancelled, preventingFillPipeAsyncfrom blocking indefinitely onstream.ReadAsyncafter aConnection: closeresponse. - WebSocket masked frames — Server now enforces RFC 6455 client-to-server masking requirement.
- Component validation on all HTTP methods —
ComponentScannerpreviously only validated form parameters onPOST; now runs on all methods. CascadingParameterModelState —FindCascadingValuenow cascades parentModelStateto child components that requestDictionary<string, string>.- 108 unit tests (up from 102).
Credits
Razor Components in CosmoApiServer is inspired by Microsoft Blazor, but implemented as a lightweight, SSR-only engine focused on raw performance and zero dependencies. The form system (EditForm, InputText, EditContext, change detection) follows Blazor's API surface while adding snapshot-based dirty tracking that Blazor SSR does not provide.
Portions of the templating engine are based on the excellent RazorSlices project by Damian Edwards, licensed under the MIT License.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Cosmo.Transport (>= 1.0.2)
- Microsoft.Extensions.Configuration (>= 10.0.1)
- Microsoft.Extensions.Configuration.CommandLine (>= 10.0.1)
- Microsoft.Extensions.Configuration.EnvironmentVariables (>= 10.0.1)
- Microsoft.Extensions.Configuration.Json (>= 10.0.1)
- Microsoft.Extensions.DependencyInjection (>= 10.0.1)
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.1)
- Microsoft.Extensions.Logging (>= 10.0.1)
- Microsoft.IdentityModel.JsonWebTokens (>= 8.16.0)
- System.IdentityModel.Tokens.Jwt (>= 8.16.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on CosmoApiServer.Core:
| Package | Downloads |
|---|---|
|
CosmoS3
Amazon S3-compatible object storage server built on CosmoApiServer.Core. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.0.1 | 104 | 3/26/2026 |
| 2.0.0 | 92 | 3/24/2026 |
| 1.8.8 | 82 | 3/19/2026 |
| 1.8.7 | 81 | 3/19/2026 |
| 1.8.6 | 97 | 3/19/2026 |
| 1.8.5 | 76 | 3/19/2026 |
| 1.8.4 | 78 | 3/19/2026 |
| 1.8.3 | 77 | 3/19/2026 |
| 1.8.2 | 76 | 3/19/2026 |
| 1.8.1 | 86 | 3/18/2026 |
| 1.8.0 | 75 | 3/18/2026 |
| 1.7.0 | 117 | 3/17/2026 |
| 1.6.3 | 91 | 3/17/2026 |
| 1.6.2 | 141 | 3/16/2026 |
| 1.6.1 | 87 | 3/14/2026 |
| 1.6.0 | 102 | 3/9/2026 |
| 1.5.0 | 82 | 3/9/2026 |
| 1.4.4 | 92 | 3/8/2026 |
| 1.4.3 | 77 | 3/8/2026 |
| 1.4.2 | 77 | 3/8/2026 |