fix: use spawn+manual read+wait instead of output() to avoid stdout pipe hang
This commit is contained in:
parent
94200b430f
commit
0edce8c555
|
|
@ -196,24 +196,51 @@ async fn run_curl(
|
||||||
|
|
||||||
cmd.arg(url);
|
cmd.arg(url);
|
||||||
|
|
||||||
let output = tokio::time::timeout(
|
let mut child = cmd.spawn().map_err(|e| format!("curl spawn error: {e}"))?;
|
||||||
std::time::Duration::from_secs_f64(timeout_secs + 2.0),
|
|
||||||
cmd.output()
|
|
||||||
).await
|
|
||||||
.map_err(|_| format!("timed out after {:.0}s", timeout_secs))?
|
|
||||||
.map_err(|e| format!("curl exec error: {e}"))?;
|
|
||||||
|
|
||||||
if !output.status.success() {
|
let stdout_handle = child.stdout.take();
|
||||||
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
|
let stderr_handle = child.stderr.take();
|
||||||
let msg = match output.status.code() {
|
|
||||||
Some(28) => format!("timed out after {:.0}s", timeout_secs),
|
// Read stdout and stderr concurrently, then wait for process exit
|
||||||
Some(6) => "DNS lookup failed".to_string(),
|
let (stdout_bytes, stderr_bytes, status) = tokio::time::timeout(
|
||||||
Some(7) => "connection refused".to_string(),
|
std::time::Duration::from_secs_f64(timeout_secs + 2.0),
|
||||||
_ => stderr.trim().to_string(),
|
async {
|
||||||
|
let stdout_fut = async {
|
||||||
|
if let Some(mut out) = stdout_handle {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
tokio::io::AsyncReadExt::read_to_end(&mut out, &mut buf).await.ok();
|
||||||
|
buf
|
||||||
|
} else { Vec::new() }
|
||||||
|
};
|
||||||
|
let stderr_fut = async {
|
||||||
|
if let Some(mut err) = stderr_handle {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
tokio::io::AsyncReadExt::read_to_end(&mut err, &mut buf).await.ok();
|
||||||
|
buf
|
||||||
|
} else { Vec::new() }
|
||||||
|
};
|
||||||
|
let (out, err) = tokio::join!(stdout_fut, stderr_fut);
|
||||||
|
let status = child.wait().await;
|
||||||
|
(out, err, status)
|
||||||
|
}
|
||||||
|
).await.map_err(|_| format!("timed out after {:.0}s", timeout_secs))?;
|
||||||
|
|
||||||
|
let exit_code = status.ok().and_then(|s| s.code()).unwrap_or(-1);
|
||||||
|
|
||||||
|
if exit_code != 0 {
|
||||||
|
let stderr = String::from_utf8_lossy(&stderr_bytes).to_string();
|
||||||
|
let msg = match exit_code {
|
||||||
|
28 => format!("timed out after {:.0}s", timeout_secs),
|
||||||
|
6 => "DNS lookup failed".to_string(),
|
||||||
|
7 => "connection refused".to_string(),
|
||||||
|
_ => stderr.trim().to_string(),
|
||||||
};
|
};
|
||||||
return Err(msg);
|
return Err(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct FakeOutput { stdout: Vec<u8>, stderr: Vec<u8> }
|
||||||
|
let output = FakeOutput { stdout: stdout_bytes, stderr: stderr_bytes };
|
||||||
|
|
||||||
// Parse curl output: headers then blank line then body
|
// Parse curl output: headers then blank line then body
|
||||||
let raw = String::from_utf8_lossy(&output.stdout).to_string();
|
let raw = String::from_utf8_lossy(&output.stdout).to_string();
|
||||||
let mut parts = raw.splitn(2, "\r\n\r\n");
|
let mut parts = raw.splitn(2, "\r\n\r\n");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue