Skip to main content

hashiverse_lib/transport/
partial_https_transport.rs

1//! # Client-only HTTPS transport
2//!
3//! A [`crate::transport::transport::TransportFactory`] that knows how to make outbound
4//! HTTPS POSTs and how to walk a
5//! [`crate::transport::bootstrap_provider::bootstrap_provider::BootstrapProvider`] for
6//! seed addresses, but does **not** know how to bind and serve inbound requests. That
7//! server-side half lives in `hashiverse-server-lib::FullHttpsTransportFactory` next to
8//! the Let's Encrypt / rustls integration.
9//!
10//! Clients that only consume the network (browser WASM, CLI tools, the Python wrapper)
11//! use this factory because they don't need the extra compile-time weight of the full
12//! server stack.
13
14use bytes::Bytes;
15use std::sync::Arc;
16use crate::transport::bootstrap_provider::bootstrap_provider::BootstrapProvider;
17use crate::transport::transport::{TransportFactory, TransportServer};
18
19/// Partial HTTPS transport factory for client-only use.
20///
21/// Provides `rpc()` (outbound HTTPS POST) and `get_bootstrap_addresses()`.
22/// Does not support `create_server()` — use `FullHttpsTransportFactory` from
23/// `hashiverse-server-lib` for that.
24#[derive(Clone)]
25pub struct PartialHttpsTransportFactory {
26    bootstrap_provider: Arc<dyn BootstrapProvider>,
27}
28
29impl PartialHttpsTransportFactory {
30    pub fn new(bootstrap_provider: Arc<dyn BootstrapProvider>) -> Self {
31        Self { bootstrap_provider }
32    }
33}
34
35#[async_trait::async_trait]
36impl TransportFactory for PartialHttpsTransportFactory {
37    async fn get_bootstrap_addresses(&self) -> Vec<String> {
38        self.bootstrap_provider.get_bootstrap_addresses().await
39    }
40
41    async fn create_server(&self, _base_path: &str, _port: u16, _force_local_network: bool) -> anyhow::Result<Arc<dyn TransportServer>> {
42        anyhow::bail!("HttpsTransportFactory is client-only and does not support create_server(). Use ServerHttpsTransportFactory from hashiverse-server-lib.")
43    }
44
45    async fn rpc(&self, address: &str, bytes: Bytes) -> anyhow::Result<Bytes> {
46        let url = format!("https://{}/", address);
47        // Build a fresh client per call to avoid stale pooled connections — the
48        // servers have aggressive idle timeouts that kill kept-alive connections.
49        let client = reqwest::ClientBuilder::new().danger_accept_invalid_certs(true).build()?;
50        let response = client.post(url).body(bytes).send().await?;
51        let response_bytes = response.bytes().await?;
52        Ok(response_bytes)
53    }
54}