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);
|
||||
|
||||
let output = tokio::time::timeout(
|
||||
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}"))?;
|
||||
let mut child = cmd.spawn().map_err(|e| format!("curl spawn error: {e}"))?;
|
||||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
|
||||
let msg = match output.status.code() {
|
||||
Some(28) => format!("timed out after {:.0}s", timeout_secs),
|
||||
Some(6) => "DNS lookup failed".to_string(),
|
||||
Some(7) => "connection refused".to_string(),
|
||||
_ => stderr.trim().to_string(),
|
||||
let stdout_handle = child.stdout.take();
|
||||
let stderr_handle = child.stderr.take();
|
||||
|
||||
// Read stdout and stderr concurrently, then wait for process exit
|
||||
let (stdout_bytes, stderr_bytes, status) = tokio::time::timeout(
|
||||
std::time::Duration::from_secs_f64(timeout_secs + 2.0),
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
let raw = String::from_utf8_lossy(&output.stdout).to_string();
|
||||
let mut parts = raw.splitn(2, "\r\n\r\n");
|
||||
|
|
|
|||
Loading…
Reference in New Issue