Binboi Docs/SDKs/Rust
Rust SDK
The Binboi Rust SDK provides async tunnel management via Tokio. Use it to embed tunnels directly in Rust applications, integration tests, or CLI tools.
Add the Dependency
code
# Cargo.toml
[dependencies]
binboi = "1"
tokio = { version = "1", features = ["full"] }Authentication
Set environment variables before running your application:
code
export BINBOI_TOKEN=tok_alice_abc123
export BINBOI_SERVER=https://tunnel.example.comOr configure in code:
code
use binboi::Client;
let client = Client::builder()
.token("tok_alice_abc123")
.server("https://tunnel.example.com")
.build()
.await?;Opening an HTTP Tunnel
code
use binboi::Client;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let client = Client::from_env().await?;
let tunnel = client
.http()
.addr(3000)
.subdomain("myapp")
.start()
.await?;
println!("Tunnel URL: {}", tunnel.url());
// Keep alive until Ctrl-C
tokio::signal::ctrl_c().await?;
tunnel.close().await?;
Ok(())
}Integrating with Axum
code
use axum::{routing::post, Json, Router};
use binboi::Client;
use serde_json::{json, Value};
use std::net::SocketAddr;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let app = Router::new().route("/webhook", post(handle_webhook));
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let listener = tokio::net::TcpListener::bind(addr).await?;
// Open Binboi tunnel
let client = Client::from_env().await?;
let tunnel = client.http().addr(3000).subdomain("axum-app").start().await?;
println!("Public URL: {}", tunnel.url());
axum::serve(listener, app).await?;
Ok(())
}
async fn handle_webhook(Json(body): Json<Value>) -> Json<Value> {
println!("Received: {body}");
Json(json!({ "ok": true }))
}TCP Tunnels
code
let tunnel = client
.tcp()
.addr(5432)
.remote_port(15432)
.start()
.await?;
println!("TCP endpoint: {}", tunnel.addr());
// TCP endpoint: tunnel.example.com:15432Tunnel Options
code
let tunnel = client
.http()
.addr(3000)
.subdomain("myapp") // Reserved subdomain
.hostname("dev.my.com") // Custom domain
.region("eu-west") // Region
.inspect(false) // Disable local inspector
.start()
.await?;Listening Directly
The SDK can accept connections itself without binding a separate TCP socket:
code
use binboi::Client;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let client = Client::from_env().await?;
let mut listener = client.http().listen().await?;
println!("Public URL: {}", listener.url());
while let Some(mut conn) = listener.accept().await {
tokio::spawn(async move {
let response = b"HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nHello";
conn.write_all(response).await.ok();
});
}
Ok(())
}Use in Tests
code
#[cfg(test)]
mod tests {
use super::*;
use binboi::Client;
#[tokio::test]
async fn test_webhook_endpoint() {
let client = Client::from_env().await.unwrap();
let tunnel = client.http().addr(3000).start().await.unwrap();
let res = reqwest::Client::new()
.post(format!("{}/webhook", tunnel.url()))
.json(&serde_json::json!({ "event": "test" }))
.send()
.await
.unwrap();
assert_eq!(res.status(), 200);
tunnel.close().await.unwrap();
}
}Error Handling
code
use binboi::{Client, Error};
match client.http().addr(3000).subdomain("taken").start().await {
Ok(tunnel) => println!("URL: {}", tunnel.url()),
Err(Error::SubdomainInUse(name)) => eprintln!("Subdomain '{name}' is taken"),
Err(Error::Auth(msg)) => eprintln!("Auth error: {msg}"),
Err(e) => eprintln!("Error: {e}"),
}Feature Flags
| Feature | Description | Default |
|---|---|---|
| default | HTTP and TCP tunnels, Tokio runtime | enabled |
| tls-native | Use native OS TLS (SChannel / Secure Transport) | disabled |
| tls-rustls | Use rustls instead of OpenSSL | disabled |