Skip to main content

ahp_ws/
lib.rs

1//! WebSocket transport adapter for the [Agent Host Protocol][spec] Rust
2//! SDK.
3//!
4//! [spec]: https://microsoft.github.io/agent-host-protocol/
5//!
6//! This crate provides [`WebSocketTransport`], an implementation of
7//! [`ahp::Transport`] backed by [`tokio-tungstenite`][tt]. It supports
8//! both `ws://` and `wss://` URLs.
9//!
10//! [tt]: https://crates.io/crates/tokio-tungstenite
11//!
12//! # TLS backends
13//!
14//! `wss://` support is selected by Cargo feature. The default,
15//! `rustls-tls-native-roots`, uses [rustls] (a pure-Rust stack, so no
16//! OpenSSL on Linux) with roots loaded from the OS trust store, which keeps
17//! dials working through a TLS-intercepting egress proxy whose CA lives in
18//! the platform store. Override it with `default-features = false` plus one
19//! of:
20//!
21//! - `native-tls` — the platform TLS stack (SChannel / Secure Transport /
22//!   OpenSSL).
23//! - `rustls-tls-native-roots` — rustls with OS-trust-store roots (default).
24//! - `rustls-tls-webpki-roots` — rustls with the bundled Mozilla root set;
25//!   does not see enterprise/proxy CAs that live only in the OS store.
26//!
27//! With no TLS feature enabled, only `ws://` works and `wss://` fails at
28//! connect time.
29//!
30//! If more than one backend ends up enabled — which Cargo feature
31//! unification can do when several crates in the graph request different
32//! ones — `native-tls` takes precedence over the rustls backends, because
33//! `tokio-tungstenite`'s automatic connector prefers it. Disable the
34//! default with `default-features = false` if you need to guarantee a
35//! rustls backend.
36//!
37//! [rustls]: https://crates.io/crates/rustls
38//!
39//! # Companion crates
40//!
41//! - [`ahp`](https://docs.rs/ahp) — the async client and reducers
42//! - [`ahp-types`](https://docs.rs/ahp-types) — wire types only
43//!
44//! # Quickstart
45//!
46//! ```no_run
47//! use ahp::{Client, ClientConfig, SubscriptionEvent};
48//! use ahp_ws::WebSocketTransport;
49//!
50//! # async fn run() -> Result<(), Box<dyn std::error::Error>> {
51//! let transport = WebSocketTransport::connect("ws://localhost:12345").await?;
52//! let client = Client::connect(transport, ClientConfig::default()).await?;
53//!
54//! client.initialize("my-client".into(), vec!["0.1.0".into()], vec!["ahp-root://".into()]).await?;
55//!
56//! let mut sub = client.attach_subscription("ahp-root://").await;
57//! while let Some(SubscriptionEvent::Action(env)) = sub.recv().await {
58//!     println!("seq={} action={:?}", env.server_seq, env.action);
59//! }
60//!
61//! client.shutdown().await;
62//! # Ok(()) }
63//! ```
64//!
65//! # Bring your own connection
66//!
67//! When you need custom TLS, headers, or a pre-existing socket, drive
68//! `tokio-tungstenite` yourself and wrap the result with
69//! [`WebSocketTransport::from_stream`]:
70//!
71//! ```no_run
72//! use ahp_ws::WebSocketTransport;
73//! use tokio_tungstenite::connect_async;
74//!
75//! # async fn run() -> Result<(), Box<dyn std::error::Error>> {
76//! let (stream, _resp) = connect_async("wss://example.com/ahp").await?;
77//! let transport = WebSocketTransport::from_stream(stream);
78//! # Ok(()) }
79//! ```
80//!
81//! # Errors
82//!
83//! Connection-time errors surface as [`WebSocketTransportError`] (URL
84//! parse or handshake failure). Once the transport is handed to
85//! [`ahp::Client`], runtime errors are reported as
86//! [`ahp::TransportError`] on the client's request and subscription
87//! futures.
88
89#![forbid(unsafe_code)]
90#![warn(missing_docs)]
91#![cfg_attr(docsrs, feature(doc_cfg))]
92
93mod transport;
94
95pub use transport::{WebSocketTransport, WebSocketTransportError};