Skip to content

feat(transport-ws): support custom TLS connector in WsConnect - #4149

Open
mehmetkr-31 wants to merge 2 commits into
alloy-rs:mainfrom
mehmetkr-31:feat/ws-custom-tls-connector
Open

feat(transport-ws): support custom TLS connector in WsConnect#4149
mehmetkr-31 wants to merge 2 commits into
alloy-rs:mainfrom
mehmetkr-31:feat/ws-custom-tls-connector

Conversation

@mehmetkr-31

Copy link
Copy Markdown

Motivation

Closes #3790.

Currently, alloy-transport-ws connects using tokio-tungstenite's default connector configuration without exposing a hook for callers to supply a custom Connector (such as native-tls, system certificate stores, or custom root CAs). While alloy-transport-http can be configured with system/native TLS certificates, WebSocket connections against endpoints with non-WebPKI or enterprise certificates fail with UnknownIssuer.

Solution

  1. Add connector: Option<Connector> field to WsConnect with builder methods .with_connector(connector) and .with_connector_opt(connector).
  2. Re-export Connector from alloy-transport-ws on non-WASM targets.
  3. Connect using tokio_tungstenite::connect_async_tls_with_config(req, self.config, false, self.connector.clone()) so custom connectors (e.g. Connector::NativeTls or Connector::Rustls with custom ClientConfig) are used when provided.
  4. Implement manual fmt::Debug for WsConnect to gracefully handle Connector not implementing Debug.
  5. Add unit tests covering connector builder methods.

PR Checklist

  • Added Tests
  • Added Documentation
  • Breaking changes

`cargo check -p alloy-transport-ws --no-default-features` stopped compiling
with the custom-connector change:

    error[E0425]: cannot find function `connect_async_tls_with_config`
                  in crate `tokio_tungstenite`

tokio-tungstenite exports the two pieces behind different gates:

    #[cfg(any(native-tls, __rustls-tls, connect))] pub use tls::Connector;
    #[cfg(any(native-tls, __rustls-tls))]          pub async fn connect_async_tls_with_config

`connect` is on by default, so `Connector` — and with it the field, the
builders and the getter — resolves in every configuration. Only the connect
function disappears once no TLS backend is selected, which is exactly what
`--no-default-features` does here.

Selects the plain `connect_async_with_config` in that case. There is no TLS
to configure without a backend, so nothing is lost: the connector is only
reachable through a feature that also brings the TLS-aware entry point.

This lane is not hypothetical — CI reaches it through the `feature-checks`
job, which runs `cargo hack check --feature-powerset --depth 1` over the
workspace and therefore builds this crate with no features at all.

Verified on x86_64 Linux and aarch64 macOS: `--no-default-features` fails
before this commit and builds after it; default and `--all-features` build
either way; `cargo hack check --feature-powerset --depth 1` passes for
alloy-transport-ws; `cargo clippy --all-targets --all-features` reports
nothing; tests pass; `cargo +nightly fmt --all --check` is clean. The one
remaining warning in the powerset run (`extern crate rustls is unused`, on
`rustls-tls` without a crypto provider) is present on the base branch too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

[Bug] WebSocket transport hardcodes webpki-roots, causing UnknownIssuer while HTTP works with system trust store

1 participant