Skip to repository content93 lines · 3.5 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T04:31:21.482Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
proxy.rs
1//! Proxied connections for the collaboration WebSocket.
2//!
3//! Proxy URLs come from settings or the environment (see
4//! `ProxySettings::proxy_url`); this module dials the proxy, wraps the
5//! connection in TLS when an `https://` proxy asks for it, and tunnels
6//! through it with `proxy_handshake`.
7
8use anyhow::{Context as _, Result};
9use http_client::Url;
10use proxy_handshake::{ProxyScheme, ProxySpec, Target};
11use tokio::net::TcpStream;
12
13pub(crate) trait AsyncReadWrite:
14 tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + 'static
15{
16}
17impl<T: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + 'static> AsyncReadWrite
18 for T
19{
20}
21
22/// Whether `NO_PROXY` in the environment excludes `host` from proxying,
23/// matching the exclusions the HTTP client already applies to its own
24/// requests.
25pub(crate) fn excluded_from_proxy(host: &str) -> bool {
26 http_client::read_no_proxy_from_env()
27 .is_some_and(|no_proxy| proxy_handshake::no_proxy_matches(&no_proxy, host))
28}
29
30pub(crate) async fn connect_proxy_stream(
31 proxy: &Url,
32 rpc_host: (&str, u16),
33) -> Result<Box<dyn AsyncReadWrite>> {
34 // If parsing the proxy URL fails, we must avoid falling back to an
35 // insecure connection. Proxies are often used in contexts where security
36 // and privacy are critical, so any fallback could expose users to
37 // significant risks.
38 let spec = ProxySpec::parse(proxy).context("parsing proxy URL")?;
39
40 let target = if spec.remote_dns() {
41 Target::Domain(rpc_host.0.to_string(), rpc_host.1)
42 } else {
43 // SOCKS4 requests carry a raw IPv4 address, so the target must
44 // resolve to one.
45 let requires_ipv4 = matches!(spec.scheme, ProxyScheme::Socks4 { .. });
46 let address = tokio::net::lookup_host(rpc_host)
47 .await
48 .with_context(|| format!("failed to lookup domain {}", rpc_host.0))?
49 .find(|address| !requires_ipv4 || address.is_ipv4())
50 .with_context(|| format!("failed to lookup domain {}", rpc_host.0))?;
51 Target::Address(address)
52 };
53
54 let stream = TcpStream::connect((spec.host.as_str(), spec.port))
55 .await
56 .context("Failed to connect to proxy")?;
57
58 let stream: Box<dyn AsyncReadWrite> = if spec.tls() {
59 Box::new(connect_tls_to_proxy(stream, &spec.host).await?)
60 } else {
61 Box::new(stream)
62 };
63
64 let stream = proxy_handshake::tokio::establish(stream, &spec, &target)
65 .await
66 .context("error connecting through proxy")?;
67 Ok(Box::new(stream))
68}
69
70#[cfg(any(target_os = "windows", target_os = "macos"))]
71async fn connect_tls_to_proxy(
72 stream: TcpStream,
73 proxy_domain: &str,
74) -> Result<tokio_native_tls::TlsStream<TcpStream>> {
75 use tokio_native_tls::{TlsConnector, native_tls};
76
77 let tls_connector = TlsConnector::from(native_tls::TlsConnector::new()?);
78 Ok(tls_connector.connect(proxy_domain, stream).await?)
79}
80
81#[cfg(not(any(target_os = "windows", target_os = "macos")))]
82async fn connect_tls_to_proxy(
83 stream: TcpStream,
84 proxy_domain: &str,
85) -> Result<tokio_rustls::client::TlsStream<TcpStream>> {
86 let proxy_domain = rustls_pki_types::ServerName::try_from(proxy_domain)
87 .context("invalid DNS name for proxy TLS")?
88 .to_owned();
89 let tls_connector =
90 tokio_rustls::TlsConnector::from(std::sync::Arc::new(http_client_tls::tls_config()));
91 Ok(tls_connector.connect(proxy_domain, stream).await?)
92}
93