//! Persistent test component shared by HTTP and socket acceptance tests. use std::{ path::{Path, PathBuf}, sync::OnceLock, }; fn write(path: PathBuf, text: &str) -> std::io::Result<()> { if std::fs::read_to_string(&path).ok().as_deref() != Some(text) { std::fs::write(path, text)?; } Ok(()) } pub fn component() -> &'static Path { static PATH: OnceLock = OnceLock::new(); PATH.get_or_init(|| { let target = std::env::var_os("CARGO_TARGET_DIR").map(PathBuf::from).unwrap_or_else(|| Path::new(env!("CARGO_TARGET_TMPDIR")).parent().unwrap().to_owned()); let directory = Path::new(env!("CARGO_TARGET_TMPDIR")).join("hook-guest"); std::fs::create_dir_all(directory.join("src")).unwrap(); std::fs::create_dir_all(directory.join("wit/deps")).unwrap(); write(directory.join("wit/process.wit"), include_str!("../wit/process.wit")).unwrap(); write(directory.join("wit/deps/io.wit"), include_str!("../wit/deps/io.wit")).unwrap(); write(directory.join("Cargo.toml"), "[package]\nname='hook-guest'\nversion='0.1.0'\nedition='2024'\n[workspace]\n[dependencies]\nwstd='=0.6.8'\nanyhow='1'\nfutures-lite='2'\nwit-bindgen='0.46'\n").unwrap(); write(directory.join("src/main.rs"), r#" use wstd::http::{Body, BodyExt, Client, Request}; mod bindings { wit_bindgen::generate!({path:"wit",world:"program",generate_all}); } #[wstd::main] async fn main() -> anyhow::Result<()> { let args: Vec<_> = std::env::args().collect(); match args[1].as_str() { "follow" => { let response=Client::new().send(Request::get(&args[2]).body(Body::empty())?).await?; println!("first-status={}",response.status().as_u16()); let location=response.headers()["location"].to_str()?.to_owned(); response.into_body().into_boxed_body().collect().await?; let response=Client::new().send(Request::get(location).body(Body::empty())?).await?; println!("status={}",response.status().as_u16()); response.into_body().into_boxed_body().collect().await?; }, "spawn" => { use bindings::sausage::process::host::{self, Request, Stdio}; let child=host::spawn(&Request {program:"/bin/guest.wasm".into(),argv:vec!["guest".into(),args[2].clone(),"GET".into()],cwd:"/".into(),env:vec![],group:None,stdin:Stdio::Null,stdout:Stdio::Inherit,stderr:Stdio::Inherit}).unwrap(); println!("child={}",child.pid()); assert_eq!(child.wait(),0); }, "listen" => { let listener = std::net::TcpListener::bind(&args[2])?; println!("ready"); let (mut stream, _) = listener.accept()?; use std::io::{Read, Write}; let mut bytes = [0;4]; stream.read_exact(&mut bytes)?; stream.write_all(&bytes)?; }, "connect" => { use std::io::{Read, Write}; match std::net::TcpStream::connect(&args[2]) { Ok(mut stream) => { stream.write_all(b"ping")?; let mut bytes=[0;4]; stream.read_exact(&mut bytes)?; println!("{}", std::str::from_utf8(&bytes)?); }, Err(_) => println!("denied"), } }, _ => { let body = if args.len() > 3 { Body::from_stream(futures_lite::stream::iter((0..args[3].parse::()?/8192).map(|_| vec![b'x';8192]))) } else { Body::empty() }; let request = Request::builder().uri(&args[1]).method(args[2].as_str()).body(body)?; match Client::new().send(request).await { Ok(response) => { println!("status={}", response.status().as_u16()); let bytes=response.into_body().into_boxed_body().collect().await?.to_bytes(); println!("{}",String::from_utf8_lossy(&bytes)); }, Err(_) => println!("error"), } } } Ok(()) } "#).unwrap(); assert!(std::process::Command::new("cargo").args(["build", "--manifest-path"]).arg(directory.join("Cargo.toml")).args(["--target", "wasm32-wasip2", "--release", "-j", "2"]).env("CARGO_TARGET_DIR", &target).status().unwrap().success(), "test guest build failed"); target.join("wasm32-wasip2/release/hook-guest.wasm") }) }