Skip to content
Draft
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
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

353 changes: 353 additions & 0 deletions crates/fetch/docs/design/README.md

Large diffs are not rendered by default.

164 changes: 164 additions & 0 deletions crates/fetch/docs/design/capability-matrix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# Transport capability matrix

Status: target capability contract. “Supported” describes available backend/native capability;
the merged implementations may still need wiring identified by this design.

This document compares the supported Hyper TLS combinations with the planned WinHTTP transport. It
separates differences that matter to libraries from backend mechanisms that should not expand the
portable `fetch` API.

The WinHTTP column describes the target contract grounded in available native mechanisms, the
merged implementation, and the executable capability probes linked from this design.

The two Hyper columns share one TLS-neutral `fetch_hyper_common` engine. `fetch_hyper_rustls` and
`fetch_hyper_native_tls` provide connector composition, not separate HTTP implementations.

## TLS and client authentication

| Capability | Hyper + rustls | Hyper + native TLS | WinHTTP + SChannel | Public treatment |
| --- | --- | --- | --- | --- |
| Platform trust | Platform verifier | Native platform trust | Native Windows trust | Baseline/invariant |
| Named client credential | Catalog can bind key material or a signing resolver | Catalog binds a materialized native identity | Catalog can bind a store selector or imported material | Baseline |
| Exportable certificate and private key binding | Supports common key encodings | Requires PKCS#8 through the current adapter | Planned import into a temporary certificate store | Transport construction |
| Non-exportable Windows-store binding | Supported through a rustls signing resolver | No equivalent current API | Supported through `CERT_CONTEXT` | Transport construction |
| Arbitrary external signing service | Supported through rustls signing traits | Unsupported | Unsupported unless it provides a compatible Windows key handle | `fetch_hyper_rustls` composition only |
| Custom verifier callback | Supported | Unsupported | No userspace callback | `fetch_hyper_rustls` composition only |
| Exact TLS server-name override while preserving request authority | Connector dials the request endpoint and supplies the override to rustls | Connector dials the request endpoint and supplies the override to native TLS | `WinHttpConnect` uses the TLS name, resolution override uses the endpoint, and replaced `Host` preserves authority | Baseline |
| Custom SAN/subject server-identity policy | Enforced before application data by a verifier | Unsupported | Cannot be safely enforced before request disclosure | Explicit portable non-goal; `fetch_hyper_rustls` only |
| Certificate or public-key pins | Implementable in a verifier | Unsupported | Cannot be safely enforced before request disclosure | Explicit portable non-goal; supporting composition crate only |
| Per-client custom trust roots | Expressible through custom rustls configuration | Not exposed by the current adapter | Uses Windows trust stores | Explicit portable non-goal; supporting composition crate only |
| TLS backend and crypto-provider selection | `fetch_hyper_rustls` | `fetch_hyper_native_tls` | SChannel is fixed | Application selects a composition dependency |
| Revocation | Required by the platform-verifier policy | Platform behavior | Must be enabled explicitly | Invariant, not a capability |

Libraries select a stable logical client-credential identifier. Applications bind that identifier
to key material, a Windows-store selector, or another transport-native provider. This makes source
modality a transport-construction concern rather than a library-facing capability axis.

## HTTP protocols

| Capability | Hyper + either TLS backend | WinHTTP | Public treatment |
| --- | --- | --- | --- |
| Portable HTTP/1.1 and HTTP/2 constraints | Supported | Supported | Baseline |
| Strictly require HTTP/2 | Supported by Hyper's HTTP/2-only mode | Supported by enabling HTTP/2 and setting `WINHTTP_OPTION_HTTP_PROTOCOL_REQUIRED` | Baseline; gRPC is a demonstrated consumer |
| Initial HTTP/2 stream receive window | Fixed or adaptive policy | OS default; a fixed window option exists | Transport-owned default, not public configuration |
| Prefer HTTP/3 | Unsupported | Supported on recent Windows with fallback | WinHTTP composition preference; portable requirements take precedence |
| Require HTTP/3 | Unsupported | Mechanically supported | Not exposed until HTTP/3 joins the portable baseline |
| Fine-grained HTTP/2 flow control | Supported | Different partial native controls | Internal transport policy |

Portable protocol configuration constrains HTTP/1.1 and HTTP/2 rather than ordering every protocol
a transport may implement. A gRPC library can require exact HTTP/2. WinHTTP enforces that with its
HTTP/2 enable flag and `WINHTTP_OPTION_HTTP_PROTOCOL_REQUIRED`; no user-space emulation is needed.
The option requires Windows 10 version 1903 or later, older than the WinHTTP transport's supported
platform baseline.

The receive window is not a protocol requirement. Its optimum depends on path bandwidth and RTT,
active stream count, response consumption, memory budget, and adaptive-window behavior. A numeric
cross-transport option would expose only part of that policy. Transports select and benchmark their
own defaults.

The portable default imposes no protocol constraint. A WinHTTP HTTP/3 preference expands its
transport candidates but cannot override an exact HTTP/1.1 or HTTP/2 library requirement. Removing
a preference is not a conflict; no transport-specific API can require HTTP/3.

## Connections

| Capability | Hyper + either TLS backend | WinHTTP | Public treatment |
| --- | --- | --- | --- |
| Fixed maximum connection lifetime | Enforced by retiring aged connections | Session-generation rollover retires the pool no later than the bound | Baseline |
| Per-connection lifetime callback | Supported by current Hyper options | No portable callback model | Transport-specific; fixed lifetime covers the demonstrated library need |
| Maximum idle age | Arbitrary duration or unlimited | Shortening is supported; longer retention depends on protocol and native scavenging | Baseline with value/protocol validation |
| Total connections per origin | Not provided by the current Hyper option | Native per-server cap | Baseline requirement, but Hyper needs a real total-concurrency implementation |
| Maximum idle connections per host | Current Hyper `max_connections` behavior | No equivalent meaning; WinHTTP's cap is total connections | Rename and keep transport-specific |
| Coarse idle HTTP/2 health check | Supported | Supported with a native minimum interval | Transport-owned policy; no public option |
| Keep-alive interval, acknowledgement timeout, and active-only mode | Supported by Hyper | Not available at the same granularity | Transport-specific |
| Multiple dispatch pools | Implemented above the transport | Can use multiple transport instances | Pipeline policy, not a transport capability |
| Avoid Nagle/delayed-ACK stalls | Set `TCP_NODELAY` on owned sockets | No setter; calibrated HTTP/1.1 measurements match `TCP_NODELAY`, HTTP/2 still needs qualification | Transport invariant with backend regression coverage |
| Kernel receive/send buffers | Configurable on owned sockets | No corresponding WinHTTP option | Operating-system default/autotuning |
| Initial TCP congestion window | Windows custom connectors can call an undocumented `WSAIoctl`; no portable equivalent | No corresponding WinHTTP option | Operating-system/network policy |

The current `ConnectionPoolOptions::max_connections` name is misleading: Hyper forwards it to
`pool_max_idle_per_host`, while downstream TVS configuration describes a maximum number of
concurrent connections per server. These are different guarantees and must become different
options rather than backend mappings of one field.

Connection idle age is value-dependent rather than a useful type-level distinction. WinHTTP can
honor an upper bound by closing earlier, but cannot promise arbitrary long HTTP/1.1 retention.
Construction validates the requested value together with the protocol requirement.

Nagle control is deliberately not configurable. General-purpose HTTP needs prompt semantic
boundaries and control frames; bulk transfers already fill segments, while protocol-aware
coalescing provides efficiency without ACK-dependent delay. The
[WinHTTP experiment](../../../fetch_winhttp/docs/nagle-behavior-experiment.md) verifies equivalent
behavior on the tested path but does not turn an undocumented implementation detail into a
platform guarantee.

Fixed socket buffers and initial congestion windows are not portable outcomes. They can defeat
kernel adaptation, consume memory per connection, or tune one network at the expense of another.
Application buffers remain separate implementation details.

## Timeouts, I/O, and platform services

| Capability | Hyper + either TLS backend | WinHTTP | Public treatment |
| --- | --- | --- | --- |
| End-to-end and attempt deadlines | Pipeline-owned | Pipeline-owned | Pipeline policy |
| Connect deadline | Wraps connector establishment | Native resolve/connect controls with different phase boundaries | Baseline after defining one observable deadline |
| Separate resolve/send/receive timers | Not exposed by the supported Hyper path | Native controls | Transport-specific |
| Streaming request and response bodies | Supported | Native upload/download paths implemented | Required `Transport` invariant |
| Full-duplex HTTP/2 | Supported | Native behavior demonstrated on Windows 11 build 26100; directional implementation required | Required invariant; retain platform compatibility coverage |

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A required invariant is being promoted from a single-host manual probe — Testing · Medium · High

The experiment records five manual runs on Windows 11 24H2 build 26100, while Microsoft only guarantees duplex behavior on “some versions.” fetch_winhttp/docs/design.md:68-75 requires every supported client/server baseline to pass before release, and implementation.md:1333-1341 describes future integration coverage, but this change adds only examples and no CI/release gate that executes them across the supported matrix. A later OS or runner change can therefore invalidate a required Transport invariant without failing the suite.

Direction: turn the decisive probe cases into gated integration tests and make supported-SKU qualification consume their results before advertising the capability.

Done when: every supported Windows baseline is automatically qualified (or rejected) and a duplex regression blocks release.

| Request trailers | Supported by Hyper body frames | No public WinHTTP send API | Fallible request feature; WinHTTP rejects before sending |
| Response trailers | Supported | Queryable after body completion, including HTTP/1.1 on the supported platform | Fallible terminal response-body frame |
| Response decompression | Transport can preserve encoded responses | Native support differs by encoding | Owned by configured fetch-level decompression; transports return encoded bodies |
| Cancellation when the request future is dropped | Supported | Planned through handle closure | Required `Transport` invariant |
| Plain HTTP opt-in | Pipeline request validation plus transport support | Supported | Pipeline policy |
| Runtime/executor selection | Hyper requires an adapter | WinHTTP owns asynchronous I/O callbacks | Transport construction |
| Proxy discovery and integrated Windows authentication | Not in the standard Hyper connector | Native WinHTTP strengths | Transport-specific application configuration |

## Demonstrated library requirements

Current downstream code demonstrates a smaller set than the theoretical backend inventory:

- gRPC requires HTTP/2 and configures a connect timeout;
- the TVS client configures exportable client key material, a server-certificate validator,
connection limits, idle age, and detailed HTTP/2 keep-alive values;
- fetch integration tests exercise fixed maximum connection lifetime.

These uses do not automatically justify preserving every existing knob:

- the TVS connection-limit setting currently maps to Hyper's idle-pool limit rather than the
configured concurrent-connection meaning and needs correction;
- detailed keep-alive timings are Hyper mechanisms unless the service can state a portable outcome
it requires;
- inherited C# settings for HTTP/2 windows, socket buffers, and initial congestion do not by
themselves demonstrate a library requirement; transport defaults remain until representative
benchmarks show a material deficit;
- the rustls `Validator` should first be reduced to platform trust plus an exact TLS server name.
Its SAN-pattern and subject-name modalities survive only if concrete TVS deployments cannot name
one DNS identity present in their certificates.

## Public surface conclusion

The current evidence does not justify capability traits or a transport-generic
`HttpClientBuilder<T>`. Every demonstrated library requirement has a common observable contract,
and exact TLS server-name mapping covers the need to reach a local endpoint while authenticating a
service DNS name. The public builder can therefore be one concrete type with the same methods for
all supported transports.

Custom server identity remains outside the proposed surface. It would cover service-defined SAN
patterns or known subject names only if TVS cannot migrate to an exact DNS identity. Hyper/rustls
supports that richer mechanism; the current native-TLS adapter and WinHTTP do not. It therefore
cannot become a portable capability. A TVS library that retains it must select a supporting
transport through `fetch_hyper_rustls` and reject the others.

Named client credentials, exact TLS server-name mapping, strict HTTP/2, fixed connection lifetime,
connect deadline, connection limits, streaming, cancellation, and HTTP/1.1/HTTP/2 constraints
belong to the baseline and do not need capability traits.

Arbitrary signers, raw verifier callbacks, and backend TLS objects stay on composition builders.
Detailed keep-alive controls, HTTP/3, proxy/WPAD, integrated authentication, phase-specific
timeouts, and pool internals stay composition-owned unless a demonstrated library need justifies a
dependency-light companion configuration crate. HTTP/2 flow-control sizing, socket buffers, and
initial congestion are transport-owned defaults rather than public options on any builder.

Construction remains fallible despite the uniform surface. Invalid values, missing named
credentials, unsupported operating-system versions, and unavailable resources are environmental
or provisioning failures; they are not evidence for a type-level transport capability.
Loading
Loading