Skip to main content

Module http

Module http 

Source
Expand description

A hyper-compatible connector that routes outbound HTTP requests over the tailnet.

This is the analog of Go tsnet.Server.HTTPClient, whose entire mechanism is &http.Client{Transport: &http.Transport{DialContext: s.Dial}} — a bare dialer injection with no extra client defaults. TailnetConnector is that injection for the Rust hyper ecosystem: given an http:// request Uri, it resolves the host as a MagicDNS name (or IPv4 literal) and dials it into the overlay (default port 80), so the request egresses over the tailnet rather than the host’s network. Redirects, pooling, and timeouts are the hyper client’s concern — the connector only supplies the transport, exactly like Go’s DialContext.

Obtain one from Device::http_connector and hand it to hyper_util::client::legacy::Client::builder(...).build(connector).

Available only with the hyper crate feature.

§TLS — this is a PLAINTEXT connector

TailnetConnector yields a plain overlay TCP stream and performs no TLS. Unlike Go’s http.Transport (which wraps the DialContext conn in TLS for https:// itself), hyper’s legacy Client does no TLS — it speaks HTTP directly over whatever stream the connector returns. So an https:// request through a bare TailnetConnector would be sent cleartext onto port 443; this connector therefore rejects https/wss URIs (with BadRequest) rather than dial them into a silent plaintext-on-TLS-port failure. Traffic over the tailnet is still WireGuard-encrypted hop-to-hop (the host’s origin IP never leaks), but there is no end-to-end TLS / peer-certificate validation.

For real HTTPS over the tailnet, wrap this connector in a TLS connector — e.g. hyper_rustls::HttpsConnectorBuilder::new().with_native_roots()?.https_or_http().enable_http1().wrap_connector(connector) — which performs the TLS handshake over the tailnet stream this connector supplies.

§IPv4-only

Like the rest of this fork’s tailnet surface, the connector is IPv4-only: hosts resolve to a tailnet IPv4 (or are dialed as an IPv4 literal). An IPv6-only destination is not reachable even with Config::enable_ipv6, unlike Device::dial.

§Example

use hyper_util::{client::legacy::Client, rt::TokioExecutor};

let dev = Device::new(
    &Config::default_with_key_file("tsrs_keys.json").await?,
    Some("YOUR_AUTH_KEY".to_owned()),
).await?;

// A hyper client that dials every (http://) request over the tailnet — the analog of Go
// `tsnet.Server.HTTPClient`. (Body type `String` here just to name the generic; use whatever
// `http_body::Body` your requests carry. For https, wrap `connector` in a TLS connector first.)
let connector = dev.http_connector().await?;
let client: Client<_, String> = Client::builder(TokioExecutor::new()).build(connector);

let resp = client.get("http://my-peer:8080/".parse()?).await?;
println!("status: {}", resp.status());

Structs§

TailnetConnector
A hyper connector that dials over the tailnet (the analog of Go http.Transport.DialContext = tsnet.Server.Dial). Build one with Device::http_connector and pass it to hyper_util::client::legacy::Client::builder(...).build(connector).
TailnetStream
The connection TailnetConnector yields: a tailnet netstack::TcpStream wrapped so it satisfies hyper’s IO + Connection requirements. TokioIo adapts the stream’s tokio AsyncRead/AsyncWrite to hyper’s rt::{Read,Write}, and the Connection impl reports the (unremarkable) connection metadata hyper needs.