fix(sidecar): use ephemeral ports for tcp/quic listen too
mycelium defaults --tcp-listen-port and --quic-listen-port to 9651 when not provided. If anything else holds 9651 (a previous test instance, a Docker container from the level-1 procedure, another mycelium running on the host), the daemon comes up far enough to serve the loopback API for a few seconds before tearing itself down on the listen failure. Pick three distinct ephemeral ports (api, tcp, quic) per spawn. Trade-off: inbound peers need the actual port number, which we already log; the user can pin via a future SidecarConfig field.
This commit is contained in:
@@ -102,8 +102,14 @@ impl SidecarHandle {
|
||||
}
|
||||
|
||||
let bin = locate_sidecar(app)?;
|
||||
let port = portpicker::pick_unused_port()
|
||||
.ok_or_else(|| AppError::Other("no free port available".into()))?;
|
||||
// Three ports: HTTP API (loopback), TCP listen, QUIC (UDP) listen.
|
||||
// mycelium defaults to 9651 for both peer-listen ports, which
|
||||
// collides if another instance (or a leftover from a previous test)
|
||||
// is already up. Always picking ephemeral ports avoids that at the
|
||||
// cost of inbound peers needing the actual port number.
|
||||
let api_port = pick_port()?;
|
||||
let tcp_port = pick_port_skip(&[api_port])?;
|
||||
let quic_port = pick_port_skip(&[api_port, tcp_port])?;
|
||||
|
||||
let data_dir = app
|
||||
.path()
|
||||
@@ -115,7 +121,11 @@ impl SidecarHandle {
|
||||
|
||||
let mut args = vec![
|
||||
"--api-addr".to_string(),
|
||||
format!("127.0.0.1:{port}"),
|
||||
format!("127.0.0.1:{api_port}"),
|
||||
"--tcp-listen-port".to_string(),
|
||||
tcp_port.to_string(),
|
||||
"--quic-listen-port".to_string(),
|
||||
quic_port.to_string(),
|
||||
"--key-file".to_string(),
|
||||
key_path.display().to_string(),
|
||||
];
|
||||
@@ -137,7 +147,11 @@ impl SidecarHandle {
|
||||
}
|
||||
}
|
||||
|
||||
info!(?bin, port, "spawning mycelium sidecar via pkexec");
|
||||
info!(
|
||||
?bin,
|
||||
api_port, tcp_port, quic_port,
|
||||
"spawning mycelium sidecar via pkexec"
|
||||
);
|
||||
|
||||
let mut cmd = elevation::elevated(&bin, &args);
|
||||
cmd.stdout(Stdio::piped())
|
||||
@@ -150,7 +164,7 @@ impl SidecarHandle {
|
||||
|
||||
// Stash before we await the health check, so a slow daemon
|
||||
// doesn't leave us with a zombie process if anything panics.
|
||||
let api_url = format!("http://127.0.0.1:{port}");
|
||||
let api_url = format!("http://127.0.0.1:{api_port}");
|
||||
*self.child.lock() = Some(child);
|
||||
*self.api_url.lock() = Some(api_url.clone());
|
||||
*self.config_path.lock() = Some(config_path);
|
||||
@@ -257,6 +271,22 @@ impl SidecarHandle {
|
||||
}
|
||||
}
|
||||
|
||||
fn pick_port() -> AppResult<u16> {
|
||||
portpicker::pick_unused_port().ok_or_else(|| AppError::Other("no free port available".into()))
|
||||
}
|
||||
|
||||
fn pick_port_skip(taken: &[u16]) -> AppResult<u16> {
|
||||
for _ in 0..16 {
|
||||
let p = pick_port()?;
|
||||
if !taken.contains(&p) {
|
||||
return Ok(p);
|
||||
}
|
||||
}
|
||||
Err(AppError::Other(
|
||||
"could not find a unique free port".into(),
|
||||
))
|
||||
}
|
||||
|
||||
/// Resolve the bundled `mycelium-<triple>` binary in both `tauri dev`
|
||||
/// (cargo manifest) and bundled (resource_dir) modes.
|
||||
fn locate_sidecar(app: &AppHandle) -> AppResult<PathBuf> {
|
||||
|
||||
Reference in New Issue
Block a user