Skip to content

Commit 1f26bb8

Browse files
committed
docs: Update README with server features
1 parent 75a0a0f commit 1f26bb8

1 file changed

Lines changed: 99 additions & 214 deletions

File tree

README.md

Lines changed: 99 additions & 214 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<div align="center">
22
<img src="docs/logo/logo.svg" alt="TurboHTTP" width="200" />
3-
<p><strong>High-performance HTTP client for .NET — built on Akka.Streams with automatic retries, caching, cookies, HTTP/2 multiplexing, and HTTP/3 (QUIC).</strong></p>
3+
<p><strong>High-performance HTTP client and server for .NET — built on Akka.Streams with full protocol support from HTTP/1.0 through HTTP/3 (QUIC).</strong></p>
44

55
[![CI](https://img.shields.io/github/actions/workflow/status/st0o0/TurboHTTP/ci.yml?label=CI)](https://github.com/st0o0/TurboHTTP/actions/workflows/ci.yml)
66
[![Release](https://img.shields.io/github/actions/workflow/status/st0o0/TurboHTTP/release.yml?label=Release)](https://github.com/st0o0/TurboHTTP/actions/workflows/release.yml)
@@ -12,293 +12,178 @@
1212

1313
## Why TurboHTTP?
1414

15-
TurboHTTP replaces `HttpClient` with a reactive, backpressure-aware HTTP pipeline built on [Akka.Streams](https://getakka.net/). Actors manage connection lifecycle while data flows through `System.Threading.Channels` — zero bytes ever touch an actor mailbox. The result: high throughput, low allocations, and a pipeline that never dies on transient errors.
15+
TurboHTTP is a reactive, backpressure-aware HTTP stack built on [Akka.Streams](https://getakka.net/). Actors manage connection lifecycle while data flows through `System.Threading.Channels` — zero bytes ever touch an actor mailbox. Both the client and the server share the same protocol layer, transport, and stream pipeline, giving you a symmetric architecture from HTTP/1.0 through HTTP/3. The result: high throughput, low allocations, and a pipeline that never dies on transient errors.
1616

1717
---
1818

1919
## Features
2020

21-
### Protocol Support
21+
### Protocol
2222

23-
- **HTTP/1.0 and HTTP/1.1** — chunked transfer encoding, keep-alive, pipelining
24-
- **HTTP/2** — binary framing, stream multiplexing, HPACK header compression, flow control
25-
- **HTTP/3 (QUIC)** — UDP-based transport, QPACK header compression, 0-RTT connection establishment
23+
- **HTTP/1.0 and HTTP/1.1** — chunked transfer, keep-alive, pipelining, h2c upgrade detection
24+
- **HTTP/2** — binary framing, stream multiplexing, HPACK compression, per-stream flow control
25+
- **HTTP/3 (QUIC)** — UDP transport, QPACK compression, 0-RTT connection establishment
26+
- **Dynamic protocol negotiation** — ALPN and HTTP/2 preface detection for automatic version selection
2627

27-
### Resilience
28+
### Client
2829

29-
- **Immortal pipeline** — transport failures, protocol violations, and corrupt data are absorbed gracefully. The stream only completes when you dispose the client. No single bad request or broken connection can take down the pipeline.
30-
- **Automatic retries** — idempotent methods (GET, PUT, DELETE, HEAD, OPTIONS) are retried automatically on transient failures. Respects `Retry-After` headers. POST and other non-idempotent methods are never retried.
31-
- **Connection pooling** — per-host connection pools with configurable limits, idle eviction, and automatic reconnect with exponential backoff. Connections are reused transparently across requests.
30+
- **Immortal pipeline** — transport failures, protocol violations, and corrupt data are absorbed gracefully; the stream only completes when you dispose the client
31+
- **Automatic retries** — idempotent methods retried on transient failures with `Retry-After` support
32+
- **Connection pooling** — per-host pools with configurable limits, idle eviction, and exponential backoff reconnect
33+
- **Redirect following** — 301/302/303/307/308 with correct method rewriting, body preservation, loop detection, and HTTPS downgrade protection
34+
- **Cookie management** — automatic storage and injection with domain/path matching, `Secure`, `HttpOnly`, `SameSite` support; pluggable via `ICookieJar`
35+
- **HTTP caching** — LRU cache with `Vary`, conditional requests (`ETag`, `Last-Modified`), freshness evaluation, and 304 merging; pluggable via `ICacheStore`
36+
- **Content encoding** — automatic gzip, deflate, and Brotli decompression; optional request compression
37+
- **100-Continue**`Expect: 100-continue` handling for large request bodies
38+
- **Alt-Svc** — alternative service discovery and connection migration
3239

33-
### HTTP Features
40+
### Server
3441

35-
- **Redirect following** — 301, 302, 303, 307, 308 with correct method rewriting (POST to GET on 303), body preservation on 307/308, loop detection, and HTTPS-to-HTTP downgrade protection. Configurable max redirects.
36-
- **Cookie management** — automatic cookie storage and injection across requests. Supports domain/path matching, `Secure`, `HttpOnly`, `SameSite`, `Max-Age`, and `Expires`. Bring your own `ICookieJar` implementation or use the built-in `CookieJar`.
37-
- **HTTP caching** — in-memory LRU cache with `Vary` support, conditional requests via `ETag`/`If-None-Match` and `Last-Modified`/`If-Modified-Since`, freshness evaluation (`max-age`, `s-maxage`, `Expires`, heuristic), and 304 response merging. Pluggable via `ICacheStore` for custom storage backends (Redis, disk, etc.).
38-
- **Content encoding** — automatic gzip, deflate, and Brotli response decompression. Optional request body compression. Can be disabled per-client if you need raw compressed bytes.
39-
- **100-Continue**`Expect: 100-continue` handling for large request bodies.
42+
- **Standalone HTTP server** — no Kestrel dependency, built entirely on Akka.Streams
43+
- **ASP.NET-style middleware pipeline** — composable `TurboRequestDelegate` middleware with `Use`, `Map`, and `Run`
44+
- **Entity gateway** — route HTTP requests to Akka.NET actors with ask/tell semantics, response mapping, and timeout support
45+
- **Routing and model binding** — attribute-based and fluent route registration with JSON body binding, query string binding, and parameter validation
46+
- **TLS/HTTPS** — SNI-based certificate selection, client certificate modes (require/allow/deny), renegotiation, and `ITlsHandshakeFeature`
47+
- **Connection management**`MaxConcurrentConnections` per listener, connection logging with wire-level hex dumps
48+
- **Per-protocol server options** — separate `Http1ServerOptions`, `Http2ServerOptions`, `Http3ServerOptions` with RFC-aligned defaults
4049

4150
### Performance
4251

43-
- **Zero-allocation internals**`MemoryPool<byte>`, `Span<T>`, `ReadOnlyMemory<byte>`, and `System.Threading.Channels` throughout the hot path
44-
- **HTTP/2 multiplexing** — multiple concurrent requests over a single TCP connection with header compression and per-stream flow control
45-
- **Backpressure** — Akka.Streams backpressure propagates end-to-end from the network to the caller, preventing buffer bloat and memory exhaustion under load
46-
- **Channel-based API**for high-throughput scenarios, bypass `SendAsync` and write/read directly to `System.Threading.Channels` for pipelined I/O
52+
- **Zero-allocation hot paths**`MemoryPool<byte>`, `Span<T>`, `ReadOnlyMemory<byte>`, and `System.Threading.Channels` throughout
53+
- **HTTP/2 multiplexing** — multiple concurrent requests over a single TCP connection with per-stream flow control
54+
- **Backpressure** — Akka.Streams backpressure propagates end-to-end from network to caller
55+
- **Channel-based API** — bypass `SendAsync` and write/read directly to `System.Threading.Channels` for pipelined I/O
4756

4857
### Extensibility
4958

50-
- **Handler pipeline**compose custom request/response transforms via `TurboHandler` subclasses or inline delegates, ordered FIFO
51-
- **Pluggable storage** — bring your own `ICookieJar` for custom cookie persistence or `ICacheStore` for external cache backends (Redis, disk, etc.)
52-
- **Distributed tracing** — built-in OpenTelemetry-compatible tracing via `TracingBidiStage` for request/response lifecycle visibility
53-
- **DI integration**first-class `IServiceCollection` support with named and typed clients, `IOptionsMonitor` for runtime configuration changes
54-
- **6,300+ tests** — unit tests, stream stage tests, acceptance tests, integration tests, API tests, and benchmarks
59+
- **Handler pipeline** — custom request/response transforms via `TurboHandler` subclasses or inline delegates
60+
- **Pluggable storage** — bring your own `ICookieJar` or `ICacheStore` for custom persistence backends
61+
- **OpenTelemetry tracing** — built-in `TracingBidiStage` for request/response lifecycle visibility
62+
- **DI integration**`IServiceCollection` support with named/typed clients and `IOptionsMonitor` for runtime config changes
63+
- **Comprehensive test suite** — unit, stream stage, acceptance, integration, API surface, and benchmark tests
5564

5665
---
5766

5867
## Getting Started
5968

60-
### Installation
61-
6269
```bash
6370
dotnet add package TurboHTTP
6471
```
6572

6673
Requires **.NET 10.0** or later.
6774

68-
### Basic Usage
69-
70-
Register and inject via dependency injection:
75+
### Client
7176

7277
```csharp
73-
using TurboHTTP;
74-
using System.Net.Http;
75-
using Microsoft.Extensions.DependencyInjection;
76-
7778
var services = new ServiceCollection();
78-
services.AddTurboHttpClient(options =>
79+
services.AddTurboHttpClient("GitHub", options =>
7980
{
80-
options.BaseAddress = new Uri("https://api.example.com");
81+
options.BaseAddress = new Uri("https://api.github.com");
8182
options.DefaultRequestVersion = HttpVersion.Version20;
82-
});
83+
})
84+
.WithRedirect()
85+
.WithCookies()
86+
.WithRetry(retry => retry.MaxRetries = 3);
8387

8488
var provider = services.BuildServiceProvider();
85-
var factory = provider.GetRequiredService<ITurboHttpClientFactory>();
86-
var client = factory.CreateClient();
87-
88-
var request = new HttpRequestMessage(HttpMethod.Get, "/users");
89-
var response = await client.SendAsync(request);
90-
91-
Console.WriteLine($"Status: {response.StatusCode}");
92-
var body = await response.Content.ReadAsStringAsync();
93-
Console.WriteLine(body);
94-
```
95-
96-
### Dependency Injection
97-
98-
Register named or typed clients with `IServiceCollection`:
99-
100-
```csharp
101-
services
102-
.AddTurboHttpClient("GitHub", options =>
103-
{
104-
options.BaseAddress = new Uri("https://api.github.com");
105-
options.ConnectTimeout = TimeSpan.FromSeconds(15);
106-
options.IdleTimeout = TimeSpan.FromSeconds(30);
107-
})
108-
.WithRedirect()
109-
.WithCookies()
110-
.WithDecompression()
111-
.WithRetry(retry => retry.MaxRetries = 3)
112-
.WithCache(cache => cache.MaxEntries = 1000);
113-
```
114-
115-
Then inject and use:
116-
117-
```csharp
118-
public class GitHubService(ITurboHttpClientFactory factory)
119-
{
120-
private readonly ITurboHttpClient _client = factory.CreateClient("GitHub");
89+
var client = provider.GetRequiredService<ITurboHttpClientFactory>().CreateClient("GitHub");
12190

122-
public async Task<string> GetRepoAsync(string owner, string repo, CancellationToken ct)
123-
{
124-
var request = new HttpRequestMessage(HttpMethod.Get, $"/repos/{owner}/{repo}");
125-
var response = await _client.SendAsync(request, ct);
126-
return await response.Content.ReadAsStringAsync(ct);
127-
}
128-
}
91+
var response = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, "/users"));
92+
Console.WriteLine(await response.Content.ReadAsStringAsync());
12993
```
13094

131-
### Channel-based API
132-
133-
For high-throughput scenarios, bypass `SendAsync` and use channels directly:
95+
### Server
13496

13597
```csharp
13698
var services = new ServiceCollection();
137-
services.AddTurboHttpClient(options =>
99+
services.AddTurboServer(server =>
138100
{
139-
options.BaseAddress = new Uri("https://api.example.com");
101+
server.Listen("https://localhost:5001", listen =>
102+
{
103+
listen.UseHttps();
104+
listen.Protocols = HttpProtocols.Http1AndHttp2;
105+
});
140106
});
141107

142-
var provider = services.BuildServiceProvider();
143-
var factory = provider.GetRequiredService<ITurboHttpClientFactory>();
144-
var client = factory.CreateClient();
145-
146-
// Fire requests
147-
await client.Requests.WriteAsync(new HttpRequestMessage(HttpMethod.Get, "/ping"));
148-
await client.Requests.WriteAsync(new HttpRequestMessage(HttpMethod.Get, "/health"));
149-
150-
// Read responses as they arrive
151-
await foreach (var response in client.Responses.ReadAllAsync())
108+
services.AddTurboRouting(routes =>
152109
{
153-
Console.WriteLine($"{response.RequestMessage!.RequestUri} -> {response.StatusCode}");
154-
}
155-
```
156-
157-
### Custom Cookie Jar
158-
159-
Implement `ICookieJar` to plug in your own cookie storage (e.g. encrypted, persistent, or shared across clients):
160-
161-
```csharp
162-
public sealed class PersistentCookieJar : ICookieJar
163-
{
164-
public void ProcessResponse(Uri requestUri, HttpResponseMessage response)
165-
{
166-
// parse Set-Cookie headers and persist to your backing store
167-
}
168-
169-
public void AddCookiesToRequest(Uri requestUri, ref HttpRequestMessage request)
170-
{
171-
// load cookies from your backing store and add Cookie header
172-
}
173-
}
174-
175-
services
176-
.AddTurboHttpClient("MyApi", options => { ... })
177-
.WithCookies(new PersistentCookieJar());
110+
routes.MapGet("/hello", () => Results.Ok("Hello from TurboHTTP!"));
111+
routes.MapTurboEntity<OrderActor>("/orders/{id}")
112+
.Ask(HttpMethod.Get, msg => new GetOrder(msg.RouteValues["id"]))
113+
.Tell(HttpMethod.Post, msg => new CreateOrder(msg.Body));
114+
});
178115
```
179116

180-
### Custom Cache Store
181-
182-
Implement `ICacheStore` to use Redis, disk, or any other backend instead of the built-in in-memory LRU cache:
117+
For more examples — channel API, custom handlers, cookie jars, cache stores, entity gateway patterns — see the [documentation site](https://turbohttp.leberkas.org/).
183118

184-
```csharp
185-
public sealed class RedisCacheStore : ICacheStore
186-
{
187-
public ICacheEntry? Get(HttpRequestMessage request) { /* Redis lookup */ }
188-
public void Put(HttpRequestMessage request, HttpResponseMessage response,
189-
IMemoryOwner<byte> bodyOwner, int bodyLength,
190-
DateTimeOffset requestTime, DateTimeOffset responseTime) { /* Redis store */ }
191-
public void Invalidate(Uri uri) { /* Redis delete */ }
192-
}
193-
194-
services
195-
.AddTurboHttpClient("MyApi", options => { ... })
196-
.WithCache(new RedisCacheStore(), cache => cache.MaxBodyBytes = 10_485_760);
197-
```
119+
---
198120

199-
### Custom Handlers
121+
## Architecture
200122

201-
Extend the pipeline with custom request/response transforms:
123+
### Client
202124

203-
```csharp
204-
public sealed class AuthHandler : TurboHandler
205-
{
206-
public override HttpRequestMessage ProcessRequest(HttpRequestMessage request)
207-
{
208-
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "my-token");
209-
return request;
210-
}
211-
}
212-
213-
services
214-
.AddTurboHttpClient("MyApi", options => { ... })
215-
.AddHandler<AuthHandler>();
216125
```
217-
218-
Or use inline delegates:
219-
220-
```csharp
221-
services
222-
.AddTurboHttpClient("MyApi")
223-
.UseRequest(request =>
224-
{
225-
request.Headers.Add("X-Request-Id", Guid.NewGuid().ToString());
226-
return request;
227-
});
126+
ITurboHttpClient (SendAsync / channel API)
127+
|
128+
Feature Pipeline Tracing > Handlers > Redirect > Cookie > Retry >
129+
Expect-Continue > Cache > ContentEncoding > Alt-Svc
130+
|
131+
Engine Version router > per-version client engines
132+
HTTP/1.0 | HTTP/1.1 | HTTP/2 | HTTP/3
133+
|
134+
Protocol Encoding/decoding, HPACK/QPACK, frame types
135+
|
136+
Transport TCP (ConnectionManagerActor > Channel<byte> > ClientByteMover)
137+
QUIC (ConnectionManagerActor > QUIC streams)
228138
```
229139

230-
### Configuration Options
231-
232-
| Option | Default | Description |
233-
|--------|---------|-------------|
234-
| `BaseAddress` | `null` | Base URI for resolving relative request URIs |
235-
| `ConnectTimeout` | 10s | Timeout for establishing a new TCP connection |
236-
| `IdleTimeout` | 10s | Time a connection may remain idle before eviction |
237-
| `ReconnectInterval` | 5s | Delay between reconnection attempts after failure |
238-
| `MaxReconnectAttempts` | 10 | Max reconnection attempts before giving up |
239-
| `MaxFrameSize` | 128 KiB | HTTP/2 maximum frame size in bytes |
240-
| `ConnectionPolicy` | `null` | Per-host connection limits and HTTP/2 multiplexing settings |
241-
| `DangerousAcceptAnyServerCertificate` | `false` | Skip TLS validation (dev/test only) |
242-
| `ServerCertificateValidationCallback` || Custom TLS certificate validation logic |
243-
| `ClientCertificates` | `null` | X.509 client certificates for mTLS |
244-
| `EnabledSslProtocols` | `SslProtocols.None` | TLS protocol versions to enable (OS default if `None`) |
245-
246-
---
247-
248-
## Architecture
140+
### Server
249141

250142
```
251-
Client Layer ITurboHttpClient (SendAsync / channel API)
252-
253-
Feature Layer Akka.Streams BidiStages — outermost to innermost:
254-
Tracing → Handlers → Redirect → Cookie → Retry →
255-
Expect-Continue → Cache → ContentEncoding → Alt-Svc
256-
257-
Engine Layer Engine (version router) → per-version engines
258-
Each engine: unified ConnectionStage + NetworkBufferBatchStage
259-
HTTP/1.0 · HTTP/1.1 · HTTP/2 · HTTP/3
260-
261-
Protocol Layer Encoding/decoding, HPACK/QPACK, frame types — all internal
262-
to the unified ConnectionStage per version
263-
264-
Transport Layer TcpConnectionStage / QuicConnectionStage
265-
├─ TCP → ConnectionManagerActor → Channel<byte> → ClientByteMover
266-
└─ QUIC → ConnectionManagerActor → QUIC streams
143+
Transport TcpListenerStage / QuicListenerStage
144+
|
145+
Connection ConnectionActor > protocol negotiation (ALPN / preface detection)
146+
|
147+
Protocol Per-version server engines
148+
HTTP/1.0 | HTTP/1.1 | HTTP/2 | HTTP/3
149+
|
150+
Context TurboHttpContext (request, response, features, connection info)
151+
|
152+
Middleware Pipeline stages: logging > routing > entity dispatch > handlers
153+
|
154+
Application TurboRequestDelegate / Actor entity gateway (ask/tell)
267155
```
268156

269-
For interactive architecture diagrams, see the [documentation site](https://turbohttp.st0o0.net/).
270-
271-
---
272-
273-
## Documentation
274-
275-
Full documentation — including feature guides, architecture deep-dives, and a comparison with `HttpClient` — is available at **[https://turbohttp.st0o0.net/](https://turbohttp.st0o0.net/)**.
157+
For interactive architecture diagrams, see the [documentation site](https://turbohttp.leberkas.org/).
276158

277159
---
278160

279161
## Building from Source
280162

281163
```bash
282-
# Restore and build
283164
dotnet restore ./src/TurboHTTP.slnx
284165
dotnet build --configuration Release ./src/TurboHTTP.slnx
285166

286-
# Run tests by project
287-
dotnet test --project ./src/TurboHTTP.Tests/TurboHTTP.Tests.csproj # unit
288-
dotnet test --project ./src/TurboHTTP.StreamTests/TurboHTTP.StreamTests.csproj # stream stages
289-
dotnet test --project ./src/TurboHTTP.AcceptanceTests/TurboHTTP.AcceptanceTests.csproj # acceptance
290-
dotnet test --project ./src/TurboHTTP.IntegrationTests/TurboHTTP.IntegrationTests.csproj # integration (network)
291-
dotnet test --project ./src/TurboHTTP.API.Tests/TurboHTTP.API.Tests.csproj # public API surface
167+
# Tests (xUnit v3 — use dotnet run, not dotnet test)
168+
dotnet run --project ./src/TurboHTTP.Tests/TurboHTTP.Tests.csproj
169+
dotnet run --project ./src/TurboHTTP.IntegrationTests/TurboHTTP.IntegrationTests.csproj
170+
dotnet run --project ./src/TurboHTTP.AcceptanceTests/TurboHTTP.AcceptanceTests.csproj
292171

293-
# Run benchmarks
172+
# Benchmarks
294173
dotnet run --configuration Release --project ./src/TurboHTTP.Benchmarks/TurboHTTP.Benchmarks.csproj
295174
```
296175

297176
---
298177

178+
## Documentation
179+
180+
Full documentation — including feature guides, architecture deep-dives, and API references — is available at **[turbohttp.st0o0.net](https://turbohttp.leberkas.org/)**.
181+
182+
---
183+
299184
## Contributing
300185

301-
Contributions are welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) for branch naming conventions, PR requirements, how to run tests locally, and recommended branch protection settings.
186+
Contributions are welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) for branch naming conventions, PR requirements, and how to run tests locally.
302187

303188
---
304189

0 commit comments

Comments
 (0)