CosmoApiServer.Core 2.0.1

dotnet add package CosmoApiServer.Core --version 2.0.1
                    
NuGet\Install-Package CosmoApiServer.Core -Version 2.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="CosmoApiServer.Core" Version="2.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CosmoApiServer.Core" Version="2.0.1" />
                    
Directory.Packages.props
<PackageReference Include="CosmoApiServer.Core" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add CosmoApiServer.Core --version 2.0.1
                    
#r "nuget: CosmoApiServer.Core, 2.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package CosmoApiServer.Core@2.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=CosmoApiServer.Core&version=2.0.1
                    
Install as a Cake Addin
#tool nuget:?package=CosmoApiServer.Core&version=2.0.1
                    
Install as a Cake Tool

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 parsingHttp11Parser uses ReadOnlySpan<byte> and SequenceReader<byte> directly over the pipe buffer.
  • Zero-allocation Headers — Headers are stored as ReadOnlyMemory<byte> and only materialized to strings if accessed.
  • Lazy DI scopeLazyScopeProvider only calls IServiceProvider.CreateScope() if a service is actually resolved.
  • Object PoolingHttpContext, HttpRequest, and HttpResponse are pooled and reused to eliminate GC pressure.
  • Async State Machine RenderingRenderTreeBuilder uses a struct-based command buffer for non-blocking, high-speed SSR.
  • Span-based routingRouteTable uses a ConcurrentDictionary cache and span-based matching for O(1) lookups.

Features

  • HTTP/1.1 keep-alive (pipelined)
  • HTTP/2 (h2c cleartext + ALPN over TLS)
  • TLS via SslStream with ALPN (h2 / http/1.1)
  • Razor Components — Full .razor support with @page, [Parameter], and CascadingParameters
  • Routable Components — Components can define their own routes via @page without a controller
  • Form Components<EditForm>, <InputText>, <InputNumber>, <InputSelect>, <InputCheckbox>, <InputTextArea>
  • Change Detection — Snapshot-based dirty tracking with EditContext, FieldIdentifier, and FieldState
  • @inject Dependency Injection — Inject services into Razor components via @inject
  • EventCallback / EventCallback<T> — Parent-child component communication
  • <CascadingValue> — Provide values to the entire descendant component tree
  • NavigationManager — Programmatic navigation and URI utilities
  • @bind Support — Two-way data binding via BindConverter
  • 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, custom IMiddleware)
  • 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 ServingHttpResponse.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's HttpResponse buffers using an optimized async state machine.
  • Routable: Use @page "/my-route" directly in your .razor files.
  • Cascading Parameters: Share state (like ModelState or User) down the component tree automatically.
  • Validation: Support for DataAnnotations with 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: @inject resolves 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);
    }
}

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 performanceChunkedBodyStream now stages multiple WriteAsync calls into a single chunk per FlushAsync, eliminating one chunk-header per write. NDJSON streaming throughput improved from ~4,300 to ~8,200 ops/s.
  • Fixed Flush() blocking — Sync Flush() on stream writers was calling .GetAwaiter().GetResult() on an async pipe flush, risking thread-pool starvation. Now a no-op; callers must use FlushAsync.
  • Fixed duplicate response headersWriteStreamingResponseAsync did not set _headersWritten, causing EnsureHeadersWritten() to append a second set of HTTP headers after the body.
  • Fixed connection lifecycleHttp11Connection now shares a CancellationTokenSource between FillPipeAsync and ProcessAsync. When either side finishes, the other is cancelled, preventing FillPipeAsync from blocking indefinitely on stream.ReadAsync after a Connection: close response.
  • WebSocket masked frames — Server now enforces RFC 6455 client-to-server masking requirement.
  • Component validation on all HTTP methodsComponentScanner previously only validated form parameters on POST; now runs on all methods.
  • CascadingParameter ModelStateFindCascadingValue now cascades parent ModelState to child components that request Dictionary<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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
Loading failed