CosmoHttpClient 5.1.1

dotnet add package CosmoHttpClient --version 5.1.1
                    
NuGet\Install-Package CosmoHttpClient -Version 5.1.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="CosmoHttpClient" Version="5.1.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CosmoHttpClient" Version="5.1.1" />
                    
Directory.Packages.props
<PackageReference Include="CosmoHttpClient" />
                    
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 CosmoHttpClient --version 5.1.1
                    
#r "nuget: CosmoHttpClient, 5.1.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 CosmoHttpClient@5.1.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=CosmoHttpClient&version=5.1.1
                    
Install as a Cake Addin
#tool nuget:?package=CosmoHttpClient&version=5.1.1
                    
Install as a Cake Tool

CosmoHttpClient

CosmoHttpClient is a .NET 10 HTTP client library built clean-sheet to outperform System.Net.Http.HttpClient while still offering the convenience layer (cookies, redirects, retries, response cache, decompression, NDJSON, telemetry) people expect from an everyday client.

Recommended surface: FastFeatureClient (handler pipeline) over FastAutoClient (auto-negotiates HTTP/1.1, HTTP/2, and HTTP/3 per origin).

using CosmoHttpClient.Fast.Auto;
using CosmoHttpClient.Fast.Features;

await using var transport = new FastAutoClient();
await using var client = new FastFeatureClient(new FastFeatureClientOptions
{
    Transport = transport,
    Handlers = new IFastFeatureHandler[]
    {
        new TelemetryHandler(new ActivitySourceTelemetry()),
        new ResponseCacheHandler(),
        new DecompressionHandler(),
        new CookieHandler(),
        new RedirectHandler(),
        new RetryHandler(),
    },
});

await using var resp = await client.GetAsync(new Uri("https://api.example.com/v1/things"));

That client picks H/1.1 vs H/2 via TLS ALPN on the first request to each origin, learns about H/3 from the response's Alt-Svc header, and falls back gracefully when QUIC is unavailable. Pay-as-you-go on every handler — empty handler list = bare-transport performance.

The four transport clients (FastClient, FastH2Client, FastH3Client, FastAutoClient) all implement IFastTransport and remain individually usable when you want to pin a protocol (gRPC needs H/2; benchmarks want a single transport).

Also in tree: CosmoWebSocket (RFC 6455), Grpc/* framing primitives, and a tools/PerfGate runner that enforces BDN-measured ratio + allocation rules on every change.

Requirements

  • .NET SDK 10.0+
  • For HTTP/3: platform QUIC support (System.Net.Quic / MsQuic availability)

The library targets net10.0.

Repository layout

Path Purpose
src/CosmoHttpClient/Fast/ FastClient + HTTP/1.1 connection pool, span-based H1 codec
src/CosmoHttpClient/Fast/Http2/ FastH2Client, HPACK, frame layer, multiplexed H/2 connection
src/CosmoHttpClient/Fast/Http3/ FastH3Client, QPACK, QUIC stream pool
src/CosmoHttpClient/Fast/Auto/ FastAutoClient (per-origin protocol cache + ALPN + Alt-Svc), AltSvcParser (RFC 7838)
src/CosmoHttpClient/Fast/Features/ Handler pipeline: FastFeatureClient, Decompression/Cookie/Redirect/Retry/ResponseCache/Telemetry handlers, JSON + NDJSON extensions
src/CosmoHttpClient/Connections/ Socket / TLS / SOCKS5 transport — used by CosmoWebSocket
src/CosmoHttpClient/WebSockets/ CosmoWebSocket (RFC 6455)
src/CosmoHttpClient/Grpc/ gRPC framing primitives
tests/CosmoHttpClient.Tests xUnit suite (~470 tests)
samples/CosmoHttpClient.Sample Runnable sample app
benchmarks/CosmoHttpClient.Benchmarks BDN benchmarks
benchmarks/perf-targets.json Perf-gate rules consumed by tools/PerfGate
tools/PerfGate BDN runner + JSON parser; exits non-zero on regression
docs/ RFCs (P1.10 streaming, P2 H/2, P4 features, P5 auto), retrospectives

Build, test, run

dotnet build --nologo
dotnet test --nologo --no-build

dotnet run --project samples/CosmoHttpClient.Sample
dotnet run --project samples/CosmoHttpClient.Sample -- --h2 https://www.cloudflare.com/
dotnet run --project samples/CosmoHttpClient.Sample -- --h3 https://www.cloudflare.com/

Run the perf gate (full BDN sweep + rule validation, ~15-20 min):

dotnet run -c Release --project tools/PerfGate
# Or evaluate against existing artifacts (no BDN re-run):
dotnet run -c Release --project tools/PerfGate -- --results gate-artifacts/results

How auto-negotiation works

FastAutoClient holds one each of FastClient, FastH2Client, and (lazily) FastH3Client, plus a per-origin (scheme, host, port) → CachedProtocol map.

  1. First request to a new https:// origin routes through FastH2Client. Its TLS handshake advertises ALPN [h2, http/1.1]; whatever the server picks gets cached. If the server picks H/1.1, the dispatcher catches NotSupportedException and demotes the request to FastClient for the same call.
  2. Every successful https:// response has its Alt-Svc header parsed (RFC 7838). On an h3 advertisement targeting the same origin port, the cache flips to Http3 for subsequent requests with TTL = min(MaxAltSvcLifetime, ma).
  3. Cached H/3 dispatch wraps the H/3 call in a try/catch for QuicException / IOException / SocketException / TimeoutException — bumps a FailureCount. After DemotionThreshold (default 3) consecutive failures, the entry sticky-demotes back to H/2 for MaxAltSvcLifetime (and Alt-Svc h3 re-upgrades are suppressed during that window so we don't oscillate).
  4. Plain http:// always picks H/1.1 (no h2c upgrade probe). Pin via PreferredProtocol = HttpVersion.Version20 if you need h2c against a server you trust to support it.

Knobs on FastAutoClientOptions:

Option Meaning
PreferredProtocol Pin every request to one protocol (Version11/20/30); skips ALPN+Alt-Svc entirely.
DisabledProtocol Never use this protocol even if advertised. Good for Version30 in environments that block UDP.
MaxAltSvcLifetime Cap on Alt-Svc-driven cache TTL + sticky-demote duration. Default 24h.
DemotionThreshold Consecutive H/3 failures before sticky demote. Default 3.
Http11ClientOptions / Http2ClientOptions / Http3ClientOptions Configure the inner sub-clients.

When to pin a transport

Use the explicit per-protocol clients (instead of FastAutoClient) when:

  • gRPC — pin to FastH2Client. gRPC requires H/2 framing; auto-negotiating away from it would break.
  • Single-protocol benchmarkstools/PerfGate rules use the explicit clients to isolate one transport at a time.
  • Forcing H/3 — for an origin you know speaks QUIC, FastH3Client skips the H/2 probe + Alt-Svc upgrade hop entirely.
  • Forcing H/1.1 on https:// — if you've measured that the H/2 negotiation adds latency you don't recover, pin to FastClient.

All four transport clients implement IFastTransport, so they all plug into FastFeatureClient the same way:

new FastFeatureClient(new FastFeatureClientOptions { Transport = new FastH2Client() });

Quick start — FastClient (HTTP/1.1)

using CosmoHttpClient.Fast;

await using var client = new FastClient();
await using var resp = await client.GetAsync(new Uri("https://api.example.com/v1/things"));
Console.WriteLine($"{resp.StatusCode}: {Encoding.UTF8.GetString(resp.Body.Span)}");

POST with JSON body and headers:

private static readonly HeaderPair[] AuthHeaders = {
    new("User-Agent", "myapp/1.0"),
    new("Content-Type", "application/json"),
    new("Authorization", $"Bearer {token}"),
};

var body = JsonSerializer.SerializeToUtf8Bytes(new { name = "widget" });
await using var resp = await client.PostAsync(uri, body, AuthHeaders);

Streaming a large response via PipeReader (Content-Length and chunked both supported):

await using var resp = await client.GetStreamAsync(new Uri("https://example.com/big.bin"));
var rdr = resp.BodyReader;
while (true)
{
    var read = await rdr.ReadAsync();
    foreach (var seg in read.Buffer) await output.WriteAsync(seg);
    rdr.AdvanceTo(read.Buffer.End);
    if (read.IsCompleted) break;
}

Lifetime contract: a streaming response borrows a pool connection until disposed. Drain-then-dispose returns the connection; dispose-without-drain marks the connection broken (wire state is unknown) and the pool drops it. Set FastClientOptions.BackgroundDrainOnDispose = true to opt into a fire-and-forget drain that recovers the connection at the cost of a background task per abandoned response.

FastClient exposes GetAsync, GetStreamAsync, PostAsync, and SendAsync / SendStreamAsync(method, uri, body, headers, ct) for arbitrary verbs. Buffered response body is ReadOnlyMemory<byte> aliasing the connection's arena, valid until the response is disposed. Headers iterate via a zero-allocation Http1HeaderEnumerator.

Quick start — FastH2Client (HTTP/2)

using CosmoHttpClient.Fast.Http2;

await using var h2 = new FastH2Client();
await using var res = await h2.GetAsync(new Uri("https://www.cloudflare.com/"));
Console.WriteLine($"H/2 {res.StatusCode}, {res.Body.Length} bytes");

One multiplexed connection per origin (h2c plain or h2 over TLS+ALPN), span-based HPACK + frame layer, friendly GetAsync / PostAsync. Custom request headers via the (uri, headers, ct) overload — names are validated + lowercased per RFC 7540 §8.1.2 (hop-by-hop and pseudo-headers throw before any frame goes on the wire).

Quick start — FastH3Client (HTTP/3)

using CosmoHttpClient.Fast.Http3;

await using var h3 = new FastH3Client();
try
{
    await using var res = await h3.GetAsync(new Uri("https://www.cloudflare.com/"));
    Console.WriteLine($"H/3 {res.StatusCode}, {res.Body.Length} bytes");
}
catch (FastH3NotSupportedException) { /* QUIC not available */ }

QUIC stream multiplexing, static + dynamic QPACK. Throws FastH3NotSupportedException on platforms without QUIC.

Handler pipeline reference

FastFeatureClient walks handlers in declared order — first runs outermost. Recommended layering:

Telemetry → ResponseCache → Decompression → Cookies → Redirects → Retries → terminal transport

Why this order:

  • Telemetry outermost so spans cover everything, including retries / redirects.
  • ResponseCache outside Decompression so the cache stores wire bytes (so the same entry serves clients with different Accept-Encoding).
  • Decompression outside Cookies / Redirects so subsequent handlers see plain bodies.
  • Retries innermost (around the transport) so a retried request does not loop the redirect / cookie machinery.

Deviate when you have a reason — the framework just hands you control.

JSON / NDJSON helpers compose on top of the same client:

var widget = await client.GetJsonAsync(uri, MyJsonContext.Default.Widget);

await foreach (var line in client.GetNdjsonAsync(uri, MyJsonContext.Default.LogLine, ct))
    Process(line);

CosmoWebSocket

using CosmoHttpClient.WebSockets;

await using var ws = await CosmoWebSocket.ConnectAsync("wss://example.com/socket");
await ws.SendAsync(payload, CosmoWebSocketMessageType.Binary, endOfMessage: true, ct);

Optional permessage-deflate, ping/pong keepalive, RFC 6455 close handshake. Built on the same Connections/ socket + TLS + SOCKS5 plumbing as the rest of the library.

Performance

Latest BDN sweep on Apple M1 / .NET 10 / Kestrel loopback. Cosmo / HttpClient ratios — values < 1.00 mean Cosmo is faster.

Benchmark Cosmo HttpClient Ratio
Data plane (write+parse, in-memory) 151 ns / 0 B n/a (vs naive) 0.74× of naive baseline
H1.1 single connection (1 KiB GET) 47.2 µs / 771 B 51.4 µs / 3.4 KB 0.92× / 4.4× less alloc
H1.1 pooled, single-threaded 42.9 µs / 946 B 51.7 µs / 3.4 KB 0.83× / 3.6× less alloc
H1.1 pooled, 8 workers × 64 reqs 4.93 ms / 187 KB 4.88 ms / 1.6 MB 1.01× / 8.7× less alloc
Friendly FastClient API (1 KiB GET) 43.4 µs / 1382 B 51.1 µs / 3.4 KB 0.85× / 2.5× less alloc
Friendly FastClient over HTTPS 55.3 µs / 3022 B 63.6 µs / 4.95 KB 0.87× / 1.7× less alloc
Friendly FastClient streaming 4 MiB 1.08 ms / 101 KB 1.09 ms / 100 KB 0.87× / parity

The architectural promise — 0 alloc/op on the data plane in steady state — is asserted by FastH1AllocDiagnosticTests on every test run, independent of BDN's amortization noise.

A separate Windows VM baseline (informational, not a gate) lives in docs/PERF-WINDOWS-BASELINE.md.

Releasing

Releases publish via .github/workflows/publish.yml on any pushed tag matching v*.

git tag v5.0.0
git push origin v5.0.0

The workflow strips the leading v, builds Release, runs the full test suite, packs CosmoHttpClient.<version>.nupkg + .snupkg, and pushes both to nuget.org via --skip-duplicate. The .snupkg carries SourceLink metadata so consumers can step into the library source from their debugger.

Configure once on GitHub: Settings → Secrets and variables → Actions → New repository secret named NUGET_API_KEY with a nuget.org API key scoped to CosmoHttpClient.

License

MIT — see 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 (2)

Showing the top 2 NuGet packages that depend on CosmoHttpClient:

Package Downloads
CosmoBlob.Client

CosmoHttpClient-backed client for CosmoKvD's /blobs/{key} surface. Drop into any .NET 10 app that wants an opaque blob store; pair with a thin IBlobStore adapter when used with CosmoMailServer. v1.0.0 swaps System.Net.Http.HttpClient for CosmoHttpClient's FastAutoClient (HTTP/1.1+2+3 auto-negotiation, span-based hot path).

CosmoMail

Lightweight .NET SMTP and IMAP client library with MIME generation, templating, attachments, inline images, and STARTTLS support.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
5.1.1 90 5/29/2026
5.1.0 803 5/24/2026
5.0.5 242 5/21/2026
5.0.4 89 5/21/2026
5.0.3 101 5/18/2026
5.0.2 109 5/18/2026
5.0.1 89 5/18/2026
5.0.0 104 5/10/2026
4.0.0 104 5/10/2026
3.0.0 99 5/9/2026
2.0.0 96 5/9/2026
1.0.0 98 5/8/2026