Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 23 additions & 13 deletions src/tunnel/custom.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Custom tunnel via an arbitrary shell command.

use anyhow::{Result, bail};
use anyhow::{Context, Result, bail};
use tokio::io::AsyncBufReadExt;
use tokio::process::Command;

Expand All @@ -27,21 +27,27 @@ pub struct CustomTunnel {
url_pattern: Option<String>,
proc: SharedProcess,
url: SharedUrl,
http_client: reqwest::Client,
}

impl CustomTunnel {
pub fn new(
start_command: String,
health_url: Option<String>,
url_pattern: Option<String>,
) -> Self {
Self {
) -> Result<Self> {
let http_client = reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(5))
.build()
.context("failed to create HTTP client for tunnel health checks")?;
Ok(Self {
start_command,
health_url,
url_pattern,
proc: new_shared_process(),
url: new_shared_url(),
}
http_client,
})
}
}

Expand Down Expand Up @@ -140,9 +146,9 @@ impl Tunnel for CustomTunnel {

async fn health_check(&self) -> bool {
if let Some(ref url) = self.health_url {
return reqwest::Client::new()
return self
.http_client
.get(url)
.timeout(std::time::Duration::from_secs(5))
.send()
.await
.is_ok();
Expand Down Expand Up @@ -173,7 +179,7 @@ mod tests {

#[tokio::test]
async fn empty_command_returns_error() {
let tunnel = CustomTunnel::new(" ".into(), None, None);
let tunnel = CustomTunnel::new(" ".into(), None, None).unwrap();
let result = tunnel.start("127.0.0.1", 8080).await;
assert!(result.is_err());
assert!(
Expand All @@ -186,7 +192,7 @@ mod tests {

#[tokio::test]
async fn start_without_pattern_returns_local() {
let tunnel = CustomTunnel::new("sleep 1".into(), None, None);
let tunnel = CustomTunnel::new("sleep 1".into(), None, None).unwrap();
let url = tunnel.start("127.0.0.1", 4455).await.unwrap();
assert_eq!(url, "http://127.0.0.1:4455");
tunnel.stop().await.unwrap();
Expand All @@ -198,7 +204,8 @@ mod tests {
"echo https://public.example".into(),
None,
Some("public.example".into()),
);
)
.unwrap();
let url = tunnel.start("localhost", 9999).await.unwrap();
assert_eq!(url, "https://public.example");
tunnel.stop().await.unwrap();
Expand All @@ -213,7 +220,8 @@ mod tests {
r"printf http://internal:1234\nhttps://real.tunnel.io/abc\n".into(),
None,
Some("tunnel.io".into()),
);
)
.unwrap();
let url = tunnel.start("localhost", 9999).await.unwrap();
assert_eq!(url, "https://real.tunnel.io/abc");
tunnel.stop().await.unwrap();
Expand All @@ -225,7 +233,8 @@ mod tests {
"echo http://{host}:{port}".into(),
None,
Some("http://".into()),
);
)
.unwrap();
let url = tunnel.start("10.1.2.3", 4321).await.unwrap();
assert_eq!(url, "http://10.1.2.3:4321");
tunnel.stop().await.unwrap();
Expand All @@ -238,7 +247,8 @@ mod tests {
"sleep 1".into(),
Some("http://192.0.2.1:9999/healthz".into()),
None,
);
)
.unwrap();
assert!(
!tunnel.health_check().await,
"Health check should fail for unreachable URL"
Expand Down Expand Up @@ -271,7 +281,7 @@ mod tests {
// `yes` floods stdout indefinitely; without the drain task the pipe
// buffer fills (64 KB) and the child blocks on write(), becoming a
// zombie. With draining the child stays alive and stop() can kill it.
let tunnel = CustomTunnel::new("yes".into(), None, None);
let tunnel = CustomTunnel::new("yes".into(), None, None).unwrap();
let url = tunnel.start("127.0.0.1", 19999).await.unwrap();
assert_eq!(url, "http://127.0.0.1:19999");

Expand Down
2 changes: 1 addition & 1 deletion src/tunnel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ pub fn create_tunnel(config: &TunnelProviderConfig) -> Result<Option<Box<dyn Tun
cu.start_command.clone(),
cu.health_url.clone(),
cu.url_pattern.clone(),
))))
)?)))
}

other => bail!(
Expand Down
Loading