toolkit: no thread panic in http_fetch, no casts, exact egress-proxy form
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
This commit is contained in:
@@ -80,7 +80,16 @@ pub fn fetch_with(curl: &Path, args: &HttpFetchArgs) -> Outcome {
|
||||
Some(r) => r,
|
||||
None => return Outcome::tool_error("http_fetch: curl has no standard error".to_string()),
|
||||
};
|
||||
let stderr_handle = std::thread::spawn(move || read_capped(stderr_reader));
|
||||
// `Builder`, not `spawn`, which panics when the system refuses a thread.
|
||||
let stderr_handle = match std::thread::Builder::new().spawn(move || read_capped(stderr_reader))
|
||||
{
|
||||
Ok(handle) => handle,
|
||||
Err(e) => {
|
||||
let _ = child.kill();
|
||||
let _ = child.wait();
|
||||
return Outcome::tool_error(format!("http_fetch: cannot start a thread: {e}"));
|
||||
}
|
||||
};
|
||||
|
||||
let mut body = Vec::new();
|
||||
let mut stdout = match child.stdout.take() {
|
||||
|
||||
@@ -35,7 +35,10 @@ pub fn read_file(args: &ReadFileArgs) -> Outcome {
|
||||
};
|
||||
|
||||
let mut buf = Vec::new();
|
||||
let n = match file.take(MAX_READ as u64 + 1).read_to_end(&mut buf) {
|
||||
let n = match file
|
||||
.take(u64::try_from(MAX_READ).map_or(u64::MAX, |n| n.saturating_add(1)))
|
||||
.read_to_end(&mut buf)
|
||||
{
|
||||
Err(e) => return tool_err("read_file", path, &e.to_string()),
|
||||
Ok(n) => n,
|
||||
};
|
||||
|
||||
@@ -33,7 +33,9 @@ impl From<std::io::Error> for InputError {
|
||||
/// (`Read::take`); more than MAX_INPUT is TooLarge.
|
||||
pub fn read_input(stdin: &mut dyn std::io::Read) -> Result<String, InputError> {
|
||||
let mut buf = Vec::new();
|
||||
let n = stdin.take(MAX_INPUT as u64 + 1).read_to_end(&mut buf)?;
|
||||
let n = stdin
|
||||
.take(u64::try_from(MAX_INPUT).map_or(u64::MAX, |n| n.saturating_add(1)))
|
||||
.read_to_end(&mut buf)?;
|
||||
if n > MAX_INPUT {
|
||||
return Err(InputError::TooLarge);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,8 @@ fn main() -> ExitCode {
|
||||
/// The `egress-proxy --socket <path> --allow <list>` form. Anything else, including a program name
|
||||
/// that is not `egress-proxy`, returns `None` so the tool form handles it.
|
||||
fn parse_egress_proxy(args: &[OsString]) -> Option<ExitCode> {
|
||||
if args.first()?.as_bytes() != b"egress-proxy" {
|
||||
// Exactly five words: a longer list is not this form, and goes to the tool form (exit 2).
|
||||
if args.len() != 5 || args.first()?.as_bytes() != b"egress-proxy" {
|
||||
return None;
|
||||
}
|
||||
// The form is `egress-proxy --socket <path> --allow <list>`, in that order.
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
//! `toolkit egress-proxy` takes exactly `--socket <path> --allow <list>`: with anything after
|
||||
//! them it is not the proxy, and exits 2 at once rather than listening (M3b review). Do not edit.
|
||||
|
||||
use std::process::{Command, Stdio};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[test]
|
||||
fn trailing_arguments_are_not_the_proxy_form() {
|
||||
let dir = std::env::temp_dir().join(format!("tk-egress-form-{}", std::process::id()));
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let socket = dir.join("egress.sock");
|
||||
let mut child = Command::new(env!("CARGO_BIN_EXE_toolkit"))
|
||||
.args([
|
||||
"egress-proxy",
|
||||
"--socket",
|
||||
socket.to_str().unwrap(),
|
||||
"--allow",
|
||||
"example.com",
|
||||
"extra",
|
||||
])
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.spawn()
|
||||
.unwrap();
|
||||
let until = Instant::now() + Duration::from_secs(3);
|
||||
let status = loop {
|
||||
if let Some(status) = child.try_wait().unwrap() {
|
||||
break status;
|
||||
}
|
||||
if Instant::now() > until {
|
||||
let _ = child.kill();
|
||||
let _ = child.wait();
|
||||
panic!("it is listening: trailing arguments were accepted");
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(20));
|
||||
};
|
||||
assert_eq!(status.code(), Some(2));
|
||||
assert!(!socket.exists(), "no socket was made");
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
Reference in New Issue
Block a user