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
16 changes: 15 additions & 1 deletion connect-lib/bin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,20 @@ async fn run() -> n0_error::Result<()> {
n0_error::anyerr!("Tunnel {tunnel_id} has no hostname after Ready")
})?;

// Verify origin is up and poll the tunnel URL every 10 seconds
// until it returns a successful (non-5xx) response. Only after
// this do we declare the tunnel ready to the user / Go supervisor.
let verify_mode = mode;
progress::verify_endpoints(
&endpoint,
&hostname,
std::time::Duration::from_secs(10),
move |label, url, elapsed, status| {
progress::render_verify(verify_mode, label, url, elapsed, status);
},
)
.await?;

// Re-fetch the up-to-date TunnelSummary for the tunnel_ready
// payload (existing contract — id, label, endpoint, hostnames,
// endpoint_id, status, elapsed_secs).
Expand All @@ -571,7 +585,7 @@ async fn run() -> n0_error::Result<()> {
);
} else {
for hostname in &tunnel.hostnames {
println!("Tunnel ready after {} sec: https://{}", elapsed, hostname);
println!("Tunnel ready: https://{}", hostname);
}
}

Expand Down
54 changes: 22 additions & 32 deletions connect-lib/bin/src/progress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,11 +245,11 @@ where

// --- verify_endpoints ---

/// Probe the origin endpoint (HTTP, best-effort) and proxy URL (HTTPS,
/// indefinite). Origin is bounded by `budget` and is non-fatal on failure.
/// Proxy retries indefinitely with exponential backoff and prints a status
/// message every 10s (e.g. `" waiting for proxy [url] (30s) ... HTTP 503"`)
/// so the user sees progress even during long settling times.
/// Probe the origin endpoint (HTTP, best-effort) and then the tunnel proxy URL
/// (HTTPS, indefinite). Origin is bounded by `budget` and is non-fatal on
/// failure. The proxy URL is checked on a fixed 10-second interval until it
/// returns a non-5xx response, printing a status line on each attempt so the
/// user sees progress during settling time.
pub async fn verify_endpoints<F>(
origin_endpoint: &str,
hostname: &str,
Expand Down Expand Up @@ -283,10 +283,8 @@ where
}
}

// Proxy probe — indefinite retry with periodic status every 10s.
// Proxy probe — fixed 10s interval, indefinite, until non-5xx.
let start = Instant::now();
let mut backoff = Duration::from_millis(250);
let mut last_status = Instant::now();
loop {
match client.get(&proxy_url).send().await {
Ok(resp) => {
Expand All @@ -295,34 +293,26 @@ where
verify_cb("proxy responding", &proxy_url, start.elapsed(), Some(status));
return Ok(());
}
if last_status.elapsed() >= Duration::from_secs(10) {
let _ = writeln!(
std::io::stderr(),
" \u{25CB} waiting for proxy [{}] ({:.0}s) ... HTTP {}",
proxy_url,
start.elapsed().as_secs_f64(),
status,
);
let _ = std::io::stderr().flush();
last_status = Instant::now();
}
let _ = writeln!(
std::io::stderr(),
" \u{25CB} waiting for tunnel [{}] ({:.0}s) ... HTTP {}",
proxy_url,
start.elapsed().as_secs_f64(),
status,
);
let _ = std::io::stderr().flush();
}
Err(_e) => {
if last_status.elapsed() >= Duration::from_secs(10) {
let _ = writeln!(
std::io::stderr(),
" \u{25CB} waiting for proxy [{}] ({:.0}s) ... no response",
proxy_url,
start.elapsed().as_secs_f64(),
);
let _ = std::io::stderr().flush();
last_status = Instant::now();
}
let _ = writeln!(
std::io::stderr(),
" \u{25CB} waiting for tunnel [{}] ({:.0}s) ... no response",
proxy_url,
start.elapsed().as_secs_f64(),
);
let _ = std::io::stderr().flush();
}
}
let sleep_dur = std::cmp::min(backoff, Duration::from_secs(2));
sleep(sleep_dur).await;
backoff = std::cmp::min(backoff * 2, Duration::from_secs(2));
sleep(Duration::from_secs(10)).await;
}
}

Expand Down