Skip to content

Design portable fetch transport API - #693

Draft
martin-kolinek wants to merge 11 commits into
mainfrom
u/makolnek/fetch-builder-redesign
Draft

martin-kolinek wants to merge 11 commits into
mainfrom
u/makolnek/fetch-builder-redesign

Conversation

@martin-kolinek

@martin-kolinek martin-kolinek commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

🤖 Defines the proposed stable API boundary for fetch before implementation work begins.

Summary

  • designs one concrete, transport-erased HttpClientBuilder around portable library requirements
  • stabilizes only the portable requirement surface and a generic typed transport-config registry
  • defines fetch_hyper_common as a reusable TLS-neutral HTTP engine
  • composes Rustls and native TLS through dependency-isolated fetch_hyper_rustls and fetch_hyper_native_tls crates
  • keeps backend-typed verifier, signer, provider, and connector APIs with their composition crates
  • introduces dependency-light config companions only after a demonstrated library need
  • unifies mTLS selection through application-provisioned logical credential identifiers
  • models independent network destination, exact TLS server name, and HTTP authority
  • removes inherited socket and HTTP/2 tuning knobs from the library-facing surface
  • gives portable HTTP/1.1/2 constraints precedence over WinHTTP''s optional HTTP/3 preference
  • defines fallible request/response trailer semantics and fetch-owned response decompression
  • requires full-duplex HTTP/2 and includes a native WinHTTP proof for known and unknown-length uploads

Backend evidence

  • adds a WinHTTP HTTP/2 probe demonstrating independent DNS routing, SNI/certificate validation, and :authority
  • adds a calibrated delayed-ACK experiment showing tested WinHTTP HTTP/1.1 small writes behave like the TCP_NODELAY control
  • records where WinHTTP behavior is measured rather than covered by a documented compatibility guarantee

Tuning policy

  • Nagle is not configurable; socket-owning transports enable TCP_NODELAY
  • HTTP/2 receive-window sizing remains transport-owned
  • kernel send/receive buffers remain under OS autotuning
  • initial congestion behavior remains OS/network policy

Comment thread crates/fetch/docs/design/README.md Outdated
Comment thread crates/fetch/docs/design/README.md Outdated
Comment thread crates/fetch/docs/design/README.md Outdated
Comment thread crates/fetch/docs/design/README.md Outdated
Comment thread crates/fetch/docs/design/README.md
Comment thread crates/fetch/docs/design/capability-matrix.md Outdated
Comment thread crates/fetch/docs/design/capability-matrix.md Outdated
Comment thread crates/fetch/docs/design/capability-matrix.md Outdated
Comment thread crates/fetch/docs/design/capability-matrix.md Outdated
Comment thread crates/fetch/docs/design/README.md
```rust,ignore
pub trait Transport: Send + Sync + 'static {
fn build(
self: Box<Self>,

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.

Suggested change
self: Box<Self>,
&self,

why not like this? The transport should still stay boxeable

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

🤖 Agreed that the trait should not expose boxing. I changed Transport to Clone + Send + Sync with validate(self, ...), and HttpClient::builder<T: Transport> now performs type erasure internally through a cloneable construction closure. Each builder build consumes a fresh transport-configuration clone; no boxed receiver is required from implementors.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

🤖 Correction after further discussion: Transport should remain dyn-compatible. The design now stores Arc<dyn Transport> internally and uses validate(self: Arc<Self>, ...), which is an object-safe receiver. HttpClient::builder<T> performs the Arc erasure internally, while runtime-selected callers may pass an erased transport directly. This avoids both Box<Self> and clone_box while allowing the factory to retain owned configuration.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

🤖 Final correction: your original &self suggestion is the cleaner contract. Transport::validate(&self, ...) is dyn-compatible and treats the transport as reusable immutable composition configuration. It returns a TransportFactory owning independent validated client state. HttpClient::builder<T> boxes internally, and builder_erased(Box<dyn Transport>) is only the explicit dynamic-construction boundary; neither Box nor Arc appears in the trait receiver.

pub fn configure(builder: HttpClientBuilder) -> Result<HttpClient> {
builder
.connection_lifetime(LIFETIME)
.client_certificate(ClientCredentialId::new("service-client"))

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.

imho, all tls specific configuration should have tls_ prefix to keep clean (reqwest did the same)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

🤖 Applied this to the broad portable builder: the methods are now tls_client_credential(...) and tls_server_name(...). I kept names concise inside TLS-specific composition builders such as fetch_hyper_rustls, where the crate/type scope already establishes that the mechanisms are TLS-specific.

Design a transport-erased HttpClientBuilder around portable library requirements, move backend mechanisms out of the public surface, and document named client credentials and TLS endpoint identity mapping.

Add executable WinHTTP probes for independent routing/TLS/authority control and Nagle behavior to ground the design in measured backend capabilities.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0c1d47b6-8039-4748-972d-20b238499d82
Require every supported transport to implement the complete portable baseline, add typed pre-build extensions for deliberate backend coupling, and make unsupported certificate policies explicit portable non-goals.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0c1d47b6-8039-4748-972d-20b238499d82
Define fetch_hyper as a reusable TLS-neutral HTTP engine and move rustls/native-tls connector construction into dependency-isolated composition crates. Limit fetch stabilization to portable requirements and the typed config registry.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0c1d47b6-8039-4748-972d-20b238499d82
Rename the proposed TLS-neutral engine to fetch_hyper_common and retain fetch_hyper only when referring to the current crate being split into common and TLS composition crates.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0c1d47b6-8039-4748-972d-20b238499d82
Give portable HTTP/1.1 and HTTP/2 requirements precedence over WinHTTP's HTTP/3 preference, place response decompression in fetch, and define fallible trailer semantics.

Add a direct WinHTTP probe demonstrating known- and unknown-length full-duplex HTTP/2 streaming and revise the implementation design around independent send and receive lanes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0c1d47b6-8039-4748-972d-20b238499d82
Reconcile the portable transport design with the merged WinHTTP implementation and current fetch APIs. Model validation plus lazy materialization, clarify migration gaps, retain dependency-isolated probes, and remove accidental ring activation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0c1d47b6-8039-4748-972d-20b238499d82
Separate target stabilization behavior from current implementation state, make transport configuration cloneable before validation, define UploadCompletion, and require platform qualification for WinHTTP duplex support.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0c1d47b6-8039-4748-972d-20b238499d82
@martin-kolinek
martin-kolinek force-pushed the u/makolnek/fetch-builder-redesign branch from a6310b1 to 2a23226 Compare September 18, 2026 15:28
Keep Transport generic and cloneable with a by-value validation method. Move boxing and type erasure behind HttpClient::builder so transport implementations do not expose boxed receivers.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0c1d47b6-8039-4748-972d-20b238499d82
Prefix portable HttpClientBuilder TLS requirements while keeping composition-scoped TLS mechanisms concise.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0c1d47b6-8039-4748-972d-20b238499d82
Store unbuilt transports as Arc<dyn Transport> and validate through an owned Arc receiver. This preserves runtime transport selection without exposing Box<Self> or clone_box APIs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0c1d47b6-8039-4748-972d-20b238499d82
Keep Transport dyn-compatible without encoding Box or Arc ownership in its receiver. Validation borrows reusable composition configuration and returns an independently owned factory.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0c1d47b6-8039-4748-972d-20b238499d82
@codecov

codecov Bot commented Sep 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.0%. Comparing base (36b9e57) to head (ba0bd5e).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #693   +/-   ##
=======================================
  Coverage   100.0%   100.0%           
=======================================
  Files         640      639    -1     
  Lines       85528    85484   -44     
=======================================
- Hits        85528    85484   -44     
Flag Coverage Δ
linux 100.0% <ø> (ø)
linux-arm 100.0% <ø> (ø)
scheduled ?
windows 100.0% <ø> (+<0.1%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants