Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 18 additions & 7 deletions docs/api/client-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ public sealed class TurboClientOptions
public Http2ClientOptions Http2 { get; init; } = new(); // HTTP/2 settings
public Http3ClientOptions Http3 { get; init; } = new(); // HTTP/3 settings

// Body buffering
public long? MaxStreamedResponseBodySize { get; set; } // null = unlimited; cap on a streamed response body
public int ResponseBodyBufferThreshold { get; set; } = 64 * 1024; // 64 KB; bodies below this are buffered in memory, at/above streamed
// Body buffering (response buffering threshold lives on Http1.MaxBufferedResponseBodySize)
public long? MaxStreamedResponseBodySize { get; set; } // null = unlimited; cap on a streamed response body
public int RequestBodyChunkSize { get; set; } = 16 * 1024; // 16 KB; chunk size when streaming a request body

// Connection pool
Expand All @@ -28,9 +27,11 @@ public sealed class TurboClientOptions
public X509CertificateCollection? ClientCertificates { get; set; }
public SslProtocols EnabledSslProtocols { get; set; } = SslProtocols.None;

// Socket options
// Socket and buffer options
public int? SocketSendBufferSize { get; set; }
public int? SocketReceiveBufferSize { get; set; }
public int ReceiveBufferHint { get; set; } = 64 * 1024; // 64 KB; internal receive buffer size hint
public int MinimumSegmentSize { get; set; } = 16 * 1024; // 16 KB; minimum segment size of the internal buffer pool

// Proxy
public bool UseProxy { get; set; } = true;
Expand Down Expand Up @@ -70,6 +71,7 @@ See [Connection Pooling guide](/client/connection-pooling) for pool lifecycle de
```csharp
public sealed class Http1ClientOptions
{
public int MaxBufferedResponseBodySize { get; set; } = 64 * 1024; // 64 KB; bodies up to this size are buffered in memory, larger are streamed
public int MaxConnectionsPerServer { get; set; } = 6;
public int MaxPipelineDepth { get; set; } = 16;
public int MaxResponseHeadersLength { get; set; } = 64; // KB
Expand All @@ -84,6 +86,7 @@ public sealed class Http1ClientOptions

| Property | Default | Description |
|----------|---------|-------------|
| `MaxBufferedResponseBodySize` | `64 * 1024` (64 KB) | Response bodies up to this size are buffered fully in memory; larger bodies are exposed as a streaming pipe |
| `MaxConnectionsPerServer` | `6` | Max concurrent TCP connections per host |
| `MaxPipelineDepth` | `16` | Max pipelined requests per connection |
| `MaxResponseHeadersLength` | `64` (KB) | Max total response header block size |
Expand All @@ -102,14 +105,17 @@ public sealed class Http2ClientOptions
public int MaxConnectionsPerServer { get; set; } = 6;
public int MaxConcurrentStreams { get; set; } = 100;
public int InitialConnectionWindowSize { get; set; } = 64 * 1024 * 1024; // 64 MB
public int InitialStreamWindowSize { get; set; } = 65535;
public int InitialStreamWindowSize { get; set; } = 1 * 1024 * 1024; // 1 MB
public int MaxStreamWindowSize { get; set; } = 16 * 1024 * 1024; // 16 MB
public double WindowScaleThresholdMultiplier { get; set; } = 1.0;
public bool EnableAdaptiveWindowScaling { get; set; } = true;
public int MaxFrameSize { get; set; } = 64 * 1024; // 64 KB
public int HeaderTableSize { get; set; } = 64 * 1024; // 64 KB
public int MaxResponseHeaderListSize { get; set; } = 64 * 1024; // 64 KB; max total size of response header list
public long MaxBufferedRequestBodySize { get; set; } = 64 * 1024; // 64 KB; bodies up to this size are serialized inline, larger are streamed
public long MaxRequestBodyBufferSize { get; set; } = 64 * 1024; // 64 KB; outbound body bytes buffered per stream before the encoder pauses
public int MaxReconnectAttempts { get; set; } = 3;
public int MaxReconnectBufferSize { get; set; } = 64; // max requests buffered during reconnection
public TimeSpan KeepAlivePingDelay { get; set; } = Timeout.InfiniteTimeSpan;
public TimeSpan KeepAlivePingTimeout { get; set; } = TimeSpan.FromSeconds(20);
public HttpKeepAlivePingPolicy KeepAlivePingPolicy { get; set; } = HttpKeepAlivePingPolicy.Always;
Expand All @@ -121,14 +127,17 @@ public sealed class Http2ClientOptions
| `MaxConnectionsPerServer` | `6` | Max concurrent TCP connections per host |
| `MaxConcurrentStreams` | `100` | Max concurrent streams per connection |
| `InitialConnectionWindowSize` | `64 * 1024 * 1024` (64 MB) | Connection-level flow control window |
| `InitialStreamWindowSize` | `65535` | Initial per-stream flow control window |
| `InitialStreamWindowSize` | `1 * 1024 * 1024` (1 MB) | Initial per-stream flow control window; grows up to `MaxStreamWindowSize` under adaptive scaling |
| `MaxStreamWindowSize` | `16 * 1024 * 1024` (16 MB) | Maximum per-stream flow control window |
| `WindowScaleThresholdMultiplier` | `1.0` | RTT multiplier controlling when to scale the stream window |
| `EnableAdaptiveWindowScaling` | `true` | Grow the stream receive window based on observed throughput |
| `MaxFrameSize` | `64 * 1024` (64 KB) | Max frame payload size |
| `HeaderTableSize` | `64 * 1024` (64 KB) | HPACK dynamic table size |
| `MaxResponseHeaderListSize` | `64 * 1024` (64 KB) | Max total size of the response header list |
| `MaxBufferedRequestBodySize` | `64 * 1024` (64 KB) | Request bodies up to this size are serialized inline; larger bodies are streamed in chunks with backpressure |
| `MaxRequestBodyBufferSize` | `64 * 1024` (64 KB) | Max outbound body bytes buffered per stream before the body encoder pauses |
| `MaxReconnectAttempts` | `3` | Max reconnect attempts on connection drop |
| `MaxReconnectBufferSize` | `64` | Max requests buffered during reconnection |
| `KeepAlivePingDelay` | `infinite` | Delay before sending keep-alive PING |
| `KeepAlivePingTimeout` | `20 s` | Timeout for PING acknowledgment |
| `KeepAlivePingPolicy` | `Always` | When to send keep-alive PINGs |
Expand Down Expand Up @@ -196,6 +205,8 @@ options.ClientCertificates = new X509CertificateCollection
|----------|---------|-------------|
| `SocketSendBufferSize` | `null` (system default) | OS socket send buffer size in bytes |
| `SocketReceiveBufferSize` | `null` (system default) | OS socket receive buffer size in bytes |
| `ReceiveBufferHint` | `64 * 1024` (64 KB) | Size hint for the internal receive buffer; larger values reduce read syscalls at the cost of memory |
| `MinimumSegmentSize` | `16 * 1024` (16 KB) | Minimum segment size of the internal buffer pool |

## Proxy Options

Expand All @@ -216,7 +227,7 @@ options.ClientCertificates = new X509CertificateCollection

| Property | Default | Description |
|----------|---------|-------------|
| `ResponseBodyBufferThreshold` | `64 * 1024` (64 KB) | Response bodies below this threshold are buffered fully in memory; at or above it the body is streamed. Shared across all protocol versions. |
| `Http1.MaxBufferedResponseBodySize` | `64 * 1024` (64 KB) | HTTP/1.x response bodies up to this size are buffered fully in memory; larger bodies are exposed as a streaming pipe |
| `MaxStreamedResponseBodySize` | `null` (unlimited) | Cap on a streamed response body; `null` means no limit |
| `RequestBodyChunkSize` | `16 * 1024` (16 KB) | Chunk size used when streaming a request body |

Expand Down
4 changes: 2 additions & 2 deletions docs/api/client.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ See [HTTP/2 & Multiplexing guide](/client/http2) for multiplexing details.

### Timeout

Per-request timeout applied by `SendAsync`. Defaults to 60 seconds. Does not affect the channel-based API:
Per-request timeout. Defaults to 60 seconds. `SendAsync` enforces it directly; requests submitted via the channel-based API get the same timeout injected as a default when no cancellation token is set on the request:

```csharp
client.Timeout = TimeSpan.FromSeconds(30);
Expand Down Expand Up @@ -114,7 +114,7 @@ Requests are matched to responses in submission order (HTTP/1.x) or by stream ID

### CancelPendingRequests

Cancels all in-flight `SendAsync` calls and clears the pending request map. Does not affect the channel-based API:
Cancels all in-flight `SendAsync` calls, clears the pending request map, and drains (disposes) any responses already buffered in the `Responses` channel:

```csharp
// Cancel everything in-flight (e.g., on application shutdown)
Expand Down
6 changes: 4 additions & 2 deletions docs/api/feature-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,17 @@ See [Automatic Retries guide](/client/retries) for which methods and status code
public sealed class CacheOptions
{
public int MaxEntries { get; set; } = 1000;
public long MaxBodySize { get; set; } = 50 * 1024 * 1024; // 50 MiB
public long MaxBodySize { get; set; } = 50 * 1024 * 1024; // 50 MiB
public long MaxTotalSize { get; set; } = 256 * 1024 * 1024; // 256 MiB
public bool SharedCache { get; set; }
}
```

| Property | Default | Description |
|----------|---------|-------------|
| `MaxEntries` | `1000` | Max number of responses in the cache |
| `MaxBodySize` | `50 * 1024 * 1024` (50 MiB) | Max total size of cached response bodies |
| `MaxBodySize` | `50 * 1024 * 1024` (50 MiB) | Max body size of a single stored response; larger responses are not cached |
| `MaxTotalSize` | `256 * 1024 * 1024` (256 MiB) | Max total size of all cached response bodies combined; least-recently-used entries are evicted when exceeded |
| `SharedCache` | `false` | Whether this is a shared cache (affecting `Cache-Control` directives) |

```csharp
Expand Down
60 changes: 55 additions & 5 deletions docs/api/server.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,10 @@ public sealed class TurboServerOptions
TimeSpan HandlerTimeout { get; set; } // default: 30s
TimeSpan HandlerGracePeriod { get; set; } // default: 5s

int RequestBodyBufferThreshold { get; set; } // default: 64 * 1024
TimeSpan BodyConsumptionTimeout { get; set; } // default: 30s
int ResponseBodyChunkSize { get; set; } // default: 16 * 1024
int MaxOutboundCoalesceCount { get; set; } // default: 32 (frames merged up to factor × 16 KiB per transport write)
bool AllowResponseHeaderCompression { get; set; } // default: true (disable to mitigate CRIME/BREACH-style attacks)

Http1ServerOptions Http1 { get; }
Http2ServerOptions Http2 { get; }
Expand Down Expand Up @@ -96,11 +97,11 @@ public sealed class TurboServerOptions
public sealed class TurboServerLimits
{
int MaxConcurrentConnections { get; set; } // default: 0 (unlimited)
int MaxConcurrentRequests { get; set; } // default: 0 (unlimited)
int MinRequestGuarantee { get; set; } // default: 10
long MaxRequestBodySize { get; set; } // default: 30 * 1024 * 1024
long MaxRequestBodySize { get; set; } // default: 30,000,000 (~28.6 MiB, matching Kestrel)
int MaxRequestHeaderCount { get; set; } // default: 100
int MaxRequestHeadersTotalSize { get; set; } // default: 32 * 1024
long MaxResponseBufferSize { get; set; } // default: 64 * 1024 (per-stream response write buffer)
long? MaxRequestBufferSize { get; set; } // default: 1 MiB (transport input buffer before backpressure; null = unlimited)
int MaxResetStreamsPerWindow { get; set; } // default: 200 (HTTP/2 Rapid Reset / CVE-2023-44487 mitigation; 0 = disabled)
TimeSpan KeepAliveTimeout { get; set; } // default: 130s
TimeSpan RequestHeadersTimeout { get; set; } // default: 30s
Expand All @@ -121,6 +122,7 @@ public sealed class TurboListenOptions(IPAddress address, ushort port)
IPAddress Address { get; }
ushort Port { get; }
HttpProtocols Protocols { get; set; } // default: Http1AndHttp2
TransportBufferOptions? Transport { get; set; } // default: null (protocol-optimized defaults)

void UseHttps();
void UseHttps(X509Certificate2 certificate);
Expand All @@ -135,6 +137,47 @@ public sealed class TurboListenOptions(IPAddress address, ushort port)

---

## Transport Buffer Options

Controls backpressure thresholds on the read/write pipes between the OS socket and the HTTP pipeline. Applied per-connection for TCP and per-stream for QUIC. Set via `TurboListenOptions.Transport`; leaving it `null` uses protocol-optimized defaults. Assigning an instance replaces the protocol defaults entirely (no per-property fallback), so set `InputPauseThreshold` and `InputResumeThreshold` explicitly — they have no initializer.

```csharp
public sealed class TransportBufferOptions
{
long InputPauseThreshold { get; set; } // bytes buffered on the read pipe before the OS socket is paused
long InputResumeThreshold { get; set; } // buffered byte count at which reading resumes (must be < pause threshold)
long OutputPauseThreshold { get; set; } // default: 64 * 1024 — bytes buffered on the write pipe before the HTTP pipeline is paused
long OutputResumeThreshold { get; set; } // default: 32 * 1024
int MinimumSegmentSize { get; set; } // minimum pipe buffer segment size
}
```

Protocol-specific defaults when `Transport` is `null`:

| Property | TCP (one pipe per connection) | QUIC (one pipe per stream) |
|----------|------------------------------|----------------------------|
| `InputPauseThreshold` | 1 MiB | 64 KiB |
| `InputResumeThreshold` | 512 KiB | 32 KiB |
| `OutputPauseThreshold` | 64 KiB | 64 KiB |
| `OutputResumeThreshold` | 32 KiB | 32 KiB |
| `MinimumSegmentSize` | 16 KiB | 4 KiB |

```csharp
options.Listen(IPAddress.Any, 8080, listen =>
{
listen.Transport = new TransportBufferOptions
{
InputPauseThreshold = 2 * 1024 * 1024,
InputResumeThreshold = 1024 * 1024,
OutputPauseThreshold = 64 * 1024,
OutputResumeThreshold = 32 * 1024,
MinimumSegmentSize = 16 * 1024
};
});
```

---

## HTTPS Options

```csharp
Expand Down Expand Up @@ -178,6 +221,7 @@ public sealed class Http1ServerOptions
int MaxRequestTargetLength { get; set; } // default: 8 * 1024
int MaxPipelinedRequests { get; set; } // default: 16
int MaxChunkExtensionLength { get; set; } // default: 4 * 1024
int MaxBufferedRequestBodySize { get; set; } // default: 64 * 1024 (bodies up to this size buffered in memory, larger streamed)
TimeSpan BodyReadTimeout { get; set; } // default: 30s
int? MaxHeaderListSize { get; set; } // default: null (uses Limits.MaxRequestHeadersTotalSize)
long? MaxRequestBodySize { get; set; } // default: null (uses Limits)
Expand All @@ -200,10 +244,15 @@ public sealed class Http2ServerOptions
int MaxConcurrentStreams { get; set; } // default: 100
int InitialConnectionWindowSize { get; set; } // default: 1 * 1024 * 1024
int InitialStreamWindowSize { get; set; } // default: 768 * 1024
int MaxStreamWindowSize { get; set; } // default: 8 * 1024 * 1024 (adaptive scaling upper bound)
double WindowScaleThresholdMultiplier { get; set; } // default: 1.0
bool EnableAdaptiveWindowScaling { get; set; } // default: true (BDP-based receive-window growth)
int MaxFrameSize { get; set; } // default: 16 * 1024
int HeaderTableSize { get; set; } // default: 4 * 1024
int? MaxHeaderListSize { get; set; } // default: null (uses Limits.MaxRequestHeadersTotalSize)
long MaxResponseBufferSize { get; set; } // default: 64 * 1024
long? MaxResponseBufferSize { get; set; } // default: null (uses Limits.MaxResponseBufferSize)
TimeSpan KeepAlivePingDelay { get; set; } // default: infinite (server-initiated keep-alive PINGs disabled)
TimeSpan KeepAlivePingTimeout { get; set; } // default: 20s (max wait for PING ACK before closing)
long? MaxRequestBodySize { get; set; } // default: null (uses Limits)
TimeSpan? KeepAliveTimeout { get; set; } // default: null (uses Limits)
TimeSpan? RequestHeadersTimeout { get; set; } // default: null (uses Limits)
Expand All @@ -225,6 +274,7 @@ public sealed class Http3ServerOptions
int? MaxHeaderListSize { get; set; } // default: null (uses Limits.MaxRequestHeadersTotalSize)
int QpackMaxTableCapacity { get; set; } // default: 0
int QpackBlockedStreams { get; set; } // default: 100
long? MaxResponseBufferSize { get; set; } // default: null (uses Limits.MaxResponseBufferSize)
long? MaxRequestBodySize { get; set; } // default: null (uses Limits)
TimeSpan? KeepAliveTimeout { get; set; } // default: null (uses Limits)
TimeSpan? RequestHeadersTimeout { get; set; } // default: null (uses Limits)
Expand Down
4 changes: 2 additions & 2 deletions docs/client/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ options.PooledConnectionLifetime = TimeSpan.FromMinutes(10);

| Property | Type | Default | Description |
| ------------------------------- | ------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `ResponseBodyBufferThreshold` | `int` | `64 * 1024` (64 KB) | Response bodies below this size are buffered fully in memory; at or above it the body is streamed (shared across all protocol versions) |
| `Http1.MaxBufferedResponseBodySize` | `int` | `64 * 1024` (64 KB) | HTTP/1.x response bodies up to this size are buffered fully in memory; larger bodies are exposed as a streaming pipe |
| `MaxStreamedResponseBodySize` | `long?` | `null` | Cap on a streamed response body; `null` = unlimited |
| `RequestBodyChunkSize` | `int` | `16 * 1024` (16 KB) | Chunk size used when streaming a request body to the server |

Expand Down Expand Up @@ -120,7 +120,7 @@ options.Http1.MaxPipelineDepth = 32;
| `Http2.MaxConnectionsPerServer` | `int` | `6` | Maximum concurrent HTTP/2 connections per host |
| `Http2.MaxConcurrentStreams` | `int` | `100` | Maximum concurrent streams per connection |
| `Http2.InitialConnectionWindowSize` | `int` | `64 * 1024 * 1024` (64 MiB) | Initial flow-control window for the whole connection |
| `Http2.InitialStreamWindowSize` | `int` | `65535` | Initial flow-control window per stream |
| `Http2.InitialStreamWindowSize` | `int` | `1 * 1024 * 1024` (1 MiB) | Initial flow-control window per stream (grows up to `MaxStreamWindowSize`) |
| `Http2.MaxStreamWindowSize` | `int` | `16 * 1024 * 1024` (16 MiB) | Upper bound for adaptive stream window growth |
| `Http2.WindowScaleThresholdMultiplier` | `double` | `1.0` | RTT multiplier that triggers a window-size increase |
| `Http2.EnableAdaptiveWindowScaling` | `bool` | `true` | Automatically grow receive windows based on measured RTT |
Expand Down
Loading