Begin implementation of wasi-http (#5929)
* Integrate experimental HTTP into wasmtime. * Reset Cargo.lock * Switch to bail!, plumb options partially. * Implement timeouts. * Remove generated files & wasm, add Makefile * Remove generated code textfile * Update crates/wasi-http/Cargo.toml Co-authored-by: Eduardo de Moura Rodrigues <16357187+eduardomourar@users.noreply.github.com> * Update crates/wasi-http/Cargo.toml Co-authored-by: Eduardo de Moura Rodrigues <16357187+eduardomourar@users.noreply.github.com> * Extract streams from request/response. * Fix read for len < buffer length. * Formatting. * types impl: swap todos for traps * streams_impl: idioms, and swap todos for traps * component impl: idioms, swap all unwraps for traps, swap all todos for traps * http impl: idiom * Remove an unnecessary mut. * Remove an unsupported function. * Switch to the tokio runtime for the HTTP request. * Add a rust example. * Update to latest wit definition * Remove example code. * wip: start writing a http test... * finish writing the outbound request example havent executed it yet * better debug output * wasi-http: some stubs required for rust rewrite of the example * add wasi_http tests to test-programs * CI: run the http tests * Fix some warnings. * bump new deps to latest releases (#3) * Add tests for wasi-http to test-programs (#2) * wip: start writing a http test... * finish writing the outbound request example havent executed it yet * better debug output * wasi-http: some stubs required for rust rewrite of the example * add wasi_http tests to test-programs * CI: run the http tests * bump new deps to latest releases h2 0.3.16 http 0.2.9 mio 0.8.6 openssl 0.10.48 openssl-sys 0.9.83 tokio 1.26.0 --------- Co-authored-by: Brendan Burns <bburns@microsoft.com> * Update crates/test-programs/tests/http_tests/runtime/wasi_http_tests.rs * Update crates/test-programs/tests/http_tests/runtime/wasi_http_tests.rs * Update crates/test-programs/tests/http_tests/runtime/wasi_http_tests.rs * wasi-http: fix cargo.toml file and publish script to work together (#4) unfortunately, the publish script doesn't use a proper toml parser (in order to not have any dependencies), so the whitespace has to be the trivial expected case. then, add wasi-http to the list of crates to publish. * Update crates/test-programs/build.rs * Switch to rustls * Cleanups. * Merge switch to rustls. * Formatting * Remove libssl install * Fix tests. * Rename wasi-http -> wasmtime-wasi-http * prtest:full Conditionalize TLS on riscv64gc. * prtest:full Fix formatting, also disable tls on s390x * prtest:full Add a path parameter to wit-bindgen, remove symlink. * prtest:full Fix tests for places where SSL isn't supported. * Update crates/wasi-http/Cargo.toml --------- Co-authored-by: Eduardo de Moura Rodrigues <16357187+eduardomourar@users.noreply.github.com> Co-authored-by: Pat Hickey <phickey@fastly.com> Co-authored-by: Pat Hickey <pat@moreproductive.org>
This commit is contained in:
@@ -9,6 +9,7 @@ license = "Apache-2.0 WITH LLVM-exception"
|
||||
|
||||
[build-dependencies]
|
||||
cfg-if = "1.0"
|
||||
cargo_metadata = "0.15.3"
|
||||
|
||||
[dev-dependencies]
|
||||
wasi-common = { workspace = true }
|
||||
@@ -23,6 +24,8 @@ anyhow = { workspace = true }
|
||||
wat = { workspace = true }
|
||||
cap-std = { workspace = true }
|
||||
tokio = { version = "1.8.0", features = ["rt-multi-thread"] }
|
||||
wasmtime-wasi-http = { workspace = true }
|
||||
|
||||
[features]
|
||||
test_programs = []
|
||||
test_programs_http = [ "wasmtime/component-model" ]
|
||||
|
||||
@@ -1,25 +1,105 @@
|
||||
#![allow(dead_code, unused_imports)]
|
||||
//! Build program to generate a program which runs all the testsuites.
|
||||
//!
|
||||
//! By generating a separate `#[test]` test for each file, we allow cargo test
|
||||
//! to automatically run the files in parallel.
|
||||
use std::fs::{read_dir, File};
|
||||
use std::io::{self, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
fn main() {
|
||||
#[cfg(feature = "test_programs")]
|
||||
wasi_tests::build_and_generate_tests()
|
||||
wasi_tests::build_and_generate_tests();
|
||||
#[cfg(feature = "test_programs_http")]
|
||||
wasi_http_tests::build_and_generate_tests();
|
||||
}
|
||||
|
||||
fn build_tests(testsuite: &str, out_dir: &Path) -> io::Result<Vec<String>> {
|
||||
let mut cmd = Command::new("cargo");
|
||||
cmd.env("CARGO_PROFILE_RELEASE_DEBUG", "1");
|
||||
cmd.env_remove("CARGO_ENCODED_RUSTFLAGS");
|
||||
cmd.args(&[
|
||||
"build",
|
||||
"--release",
|
||||
"--target=wasm32-wasi",
|
||||
"--target-dir",
|
||||
out_dir.to_str().unwrap(),
|
||||
])
|
||||
.stdout(Stdio::inherit())
|
||||
.stderr(Stdio::inherit())
|
||||
.current_dir(testsuite);
|
||||
let output = cmd.output()?;
|
||||
|
||||
let status = output.status;
|
||||
if !status.success() {
|
||||
panic!(
|
||||
"Building tests failed: exit code: {}",
|
||||
status.code().unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
let meta = cargo_metadata::MetadataCommand::new()
|
||||
.manifest_path(PathBuf::from(testsuite).join("Cargo.toml"))
|
||||
.exec()
|
||||
.expect("cargo metadata");
|
||||
|
||||
Ok(meta
|
||||
.packages
|
||||
.iter()
|
||||
.find(|p| p.name == testsuite)
|
||||
.unwrap()
|
||||
.targets
|
||||
.iter()
|
||||
.filter(|t| t.kind == ["bin"])
|
||||
.map(|t| t.name.clone())
|
||||
.collect::<Vec<String>>())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn test_directory(
|
||||
out: &mut File,
|
||||
test_binaries: &[String],
|
||||
testsuite: &str,
|
||||
runtime: &str,
|
||||
out_dir: &Path,
|
||||
mut write_testsuite_tests: impl FnMut(&mut File, &Path, &str) -> io::Result<()>,
|
||||
) -> io::Result<()> {
|
||||
writeln!(
|
||||
out,
|
||||
"mod {} {{",
|
||||
Path::new(testsuite)
|
||||
.file_stem()
|
||||
.expect("testsuite filename should have a stem")
|
||||
.to_str()
|
||||
.expect("testsuite filename should be representable as a string")
|
||||
.replace("-", "_")
|
||||
)?;
|
||||
writeln!(
|
||||
out,
|
||||
" use super::{{runtime::{} as runtime, utils, setup_log}};",
|
||||
runtime
|
||||
)?;
|
||||
for test_binary in test_binaries {
|
||||
let binary_path = out_dir
|
||||
.join("wasm32-wasi")
|
||||
.join("release")
|
||||
.join(format!("{}.wasm", test_binary.replace("-", "_")));
|
||||
write_testsuite_tests(out, &binary_path, testsuite)?;
|
||||
}
|
||||
writeln!(out, "}}")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "test_programs")]
|
||||
mod wasi_tests {
|
||||
use super::*;
|
||||
use std::env;
|
||||
use std::fs::{read_dir, File};
|
||||
use std::io::{self, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
pub(super) fn build_and_generate_tests() {
|
||||
// Validate if any of test sources are present and if they changed
|
||||
// This should always work since there is no submodule to init anymore
|
||||
let bin_tests = std::fs::read_dir("wasi-tests/src/bin").unwrap();
|
||||
let bin_tests = read_dir("wasi-tests/src/bin").unwrap();
|
||||
for test in bin_tests {
|
||||
if let Ok(test_file) = test {
|
||||
let test_file_path = test_file
|
||||
@@ -38,90 +118,25 @@ mod wasi_tests {
|
||||
);
|
||||
let mut out =
|
||||
File::create(out_dir.join("wasi_tests.rs")).expect("error generating test source file");
|
||||
build_tests("wasi-tests", &out_dir).expect("building tests");
|
||||
test_directory(&mut out, "wasi-cap-std-sync", "cap_std_sync", &out_dir)
|
||||
.expect("generating wasi-cap-std-sync tests");
|
||||
test_directory(&mut out, "wasi-tokio", "tokio", &out_dir)
|
||||
.expect("generating wasi-tokio tests");
|
||||
}
|
||||
|
||||
fn build_tests(testsuite: &str, out_dir: &Path) -> io::Result<()> {
|
||||
let mut cmd = Command::new("cargo");
|
||||
cmd.env("CARGO_PROFILE_RELEASE_DEBUG", "1");
|
||||
cmd.env_remove("CARGO_ENCODED_RUSTFLAGS");
|
||||
cmd.args(&[
|
||||
"build",
|
||||
"--release",
|
||||
"--target=wasm32-wasi",
|
||||
"--target-dir",
|
||||
out_dir.to_str().unwrap(),
|
||||
])
|
||||
.stdout(Stdio::inherit())
|
||||
.stderr(Stdio::inherit())
|
||||
.current_dir(testsuite);
|
||||
let output = cmd.output()?;
|
||||
|
||||
let status = output.status;
|
||||
if !status.success() {
|
||||
panic!(
|
||||
"Building tests failed: exit code: {}",
|
||||
status.code().unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_directory(
|
||||
out: &mut File,
|
||||
testsuite: &str,
|
||||
runtime: &str,
|
||||
out_dir: &Path,
|
||||
) -> io::Result<()> {
|
||||
let mut dir_entries: Vec<_> = read_dir(out_dir.join("wasm32-wasi/release"))
|
||||
.expect("reading testsuite directory")
|
||||
.map(|r| r.expect("reading testsuite directory entry"))
|
||||
.filter(|dir_entry| {
|
||||
let p = dir_entry.path();
|
||||
if let Some(ext) = p.extension() {
|
||||
// Only look at wast files.
|
||||
if ext == "wasm" {
|
||||
// Ignore files starting with `.`, which could be editor temporary files
|
||||
if let Some(stem) = p.file_stem() {
|
||||
if let Some(stemstr) = stem.to_str() {
|
||||
if !stemstr.starts_with('.') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
})
|
||||
.collect();
|
||||
|
||||
dir_entries.sort_by_key(|dir| dir.path());
|
||||
|
||||
writeln!(
|
||||
out,
|
||||
"mod {} {{",
|
||||
Path::new(testsuite)
|
||||
.file_stem()
|
||||
.expect("testsuite filename should have a stem")
|
||||
.to_str()
|
||||
.expect("testsuite filename should be representable as a string")
|
||||
.replace("-", "_")
|
||||
)?;
|
||||
writeln!(
|
||||
out,
|
||||
" use super::{{runtime::{} as runtime, utils, setup_log}};",
|
||||
runtime
|
||||
)?;
|
||||
for dir_entry in dir_entries {
|
||||
write_testsuite_tests(out, &dir_entry.path(), testsuite)?;
|
||||
}
|
||||
writeln!(out, "}}")?;
|
||||
Ok(())
|
||||
let test_binaries = build_tests("wasi-tests", &out_dir).expect("building tests");
|
||||
test_directory(
|
||||
&mut out,
|
||||
&test_binaries,
|
||||
"wasi-cap-std-sync",
|
||||
"cap_std_sync",
|
||||
&out_dir,
|
||||
write_testsuite_tests,
|
||||
)
|
||||
.expect("generating wasi-cap-std-sync tests");
|
||||
test_directory(
|
||||
&mut out,
|
||||
&test_binaries,
|
||||
"wasi-tokio",
|
||||
"tokio",
|
||||
&out_dir,
|
||||
write_testsuite_tests,
|
||||
)
|
||||
.expect("generating wasi-tokio tests");
|
||||
}
|
||||
|
||||
fn write_testsuite_tests(out: &mut File, path: &Path, testsuite: &str) -> io::Result<()> {
|
||||
@@ -277,3 +292,74 @@ mod wasi_tests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "test_programs_http")]
|
||||
mod wasi_http_tests {
|
||||
use super::*;
|
||||
use std::env;
|
||||
|
||||
pub(super) fn build_and_generate_tests() {
|
||||
// Validate if any of test sources are present and if they changed
|
||||
// This should always work since there is no submodule to init anymore
|
||||
let bin_tests = read_dir("wasi-http-tests/src/bin").unwrap();
|
||||
for test in bin_tests {
|
||||
if let Ok(test_file) = test {
|
||||
let test_file_path = test_file
|
||||
.path()
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.expect("test file path");
|
||||
println!("cargo:rerun-if-changed={}", test_file_path);
|
||||
}
|
||||
}
|
||||
println!("cargo:rerun-if-changed=wasi-http-tests/Cargo.toml");
|
||||
println!("cargo:rerun-if-changed=wasi-http-tests/src/lib.rs");
|
||||
// Build tests to OUT_DIR (target/*/build/wasi-common-*/out/wasm32-wasi/release/*.wasm)
|
||||
let out_dir = PathBuf::from(
|
||||
env::var("OUT_DIR").expect("The OUT_DIR environment variable must be set"),
|
||||
);
|
||||
let mut out = File::create(out_dir.join("wasi_http_tests.rs"))
|
||||
.expect("error generating test source file");
|
||||
|
||||
let test_binaries = build_tests("wasi-http-tests", &out_dir).expect("building tests");
|
||||
test_directory(
|
||||
&mut out,
|
||||
&test_binaries,
|
||||
"wasi-http-tests",
|
||||
"wasi_http_tests",
|
||||
&out_dir,
|
||||
write_testsuite_tests,
|
||||
)
|
||||
.expect("generating wasi-cap-std-sync tests");
|
||||
}
|
||||
|
||||
fn write_testsuite_tests(out: &mut File, path: &Path, _testsuite: &str) -> io::Result<()> {
|
||||
let stemstr = path
|
||||
.file_stem()
|
||||
.expect("file_stem")
|
||||
.to_str()
|
||||
.expect("to_str");
|
||||
|
||||
writeln!(out, " #[test]")?;
|
||||
let test_fn_name = stemstr.replace("-", "_");
|
||||
writeln!(out, " fn r#{}() -> anyhow::Result<()> {{", test_fn_name,)?;
|
||||
writeln!(out, " setup_log();")?;
|
||||
writeln!(
|
||||
out,
|
||||
" let path = std::path::Path::new(r#\"{}\"#);",
|
||||
path.display()
|
||||
)?;
|
||||
writeln!(out, " let data = wat::parse_file(path)?;")?;
|
||||
writeln!(
|
||||
out,
|
||||
" let bin_name = utils::extract_exec_name_from_path(path)?;"
|
||||
)?;
|
||||
writeln!(
|
||||
out,
|
||||
" runtime::instantiate_inherit_stdio(&data, &bin_name, None)",
|
||||
)?;
|
||||
writeln!(out, " }}")?;
|
||||
writeln!(out)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
12
crates/test-programs/tests/http_tests/main.rs
Normal file
12
crates/test-programs/tests/http_tests/main.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
#![cfg(feature = "test_programs_http")]
|
||||
use std::sync::Once;
|
||||
mod runtime;
|
||||
mod utils;
|
||||
|
||||
static LOG_INIT: Once = Once::new();
|
||||
|
||||
fn setup_log() {
|
||||
LOG_INIT.call_once(tracing_subscriber::fmt::init)
|
||||
}
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/wasi_http_tests.rs"));
|
||||
1
crates/test-programs/tests/http_tests/runtime/mod.rs
Normal file
1
crates/test-programs/tests/http_tests/runtime/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod wasi_http_tests;
|
||||
@@ -0,0 +1,39 @@
|
||||
use anyhow::Context;
|
||||
use std::path::Path;
|
||||
use wasmtime::{Config, Engine, Linker, Module, Store};
|
||||
use wasmtime_wasi::{sync::WasiCtxBuilder, WasiCtx};
|
||||
use wasmtime_wasi_http::WasiHttp;
|
||||
|
||||
pub fn instantiate_inherit_stdio(
|
||||
data: &[u8],
|
||||
bin_name: &str,
|
||||
_workspace: Option<&Path>,
|
||||
) -> anyhow::Result<()> {
|
||||
let config = Config::new();
|
||||
let engine = Engine::new(&config)?;
|
||||
let module = Module::new(&engine, &data).context("failed to create wasm module")?;
|
||||
let mut linker = Linker::new(&engine);
|
||||
|
||||
struct Ctx {
|
||||
wasi: WasiCtx,
|
||||
http: WasiHttp,
|
||||
}
|
||||
|
||||
wasmtime_wasi::sync::add_to_linker(&mut linker, |cx: &mut Ctx| &mut cx.wasi)?;
|
||||
wasmtime_wasi_http::add_to_linker(&mut linker, |cx: &mut Ctx| &mut cx.http)?;
|
||||
|
||||
// Create our wasi context.
|
||||
let builder = WasiCtxBuilder::new().inherit_stdio().arg(bin_name)?;
|
||||
|
||||
let mut store = Store::new(
|
||||
&engine,
|
||||
Ctx {
|
||||
wasi: builder.build(),
|
||||
http: WasiHttp::new(),
|
||||
},
|
||||
);
|
||||
|
||||
let instance = linker.instantiate(&mut store, &module)?;
|
||||
let start = instance.get_typed_func::<(), ()>(&mut store, "_start")?;
|
||||
start.call(&mut store, ())
|
||||
}
|
||||
13
crates/test-programs/tests/http_tests/utils.rs
Normal file
13
crates/test-programs/tests/http_tests/utils.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
use std::path::Path;
|
||||
|
||||
pub fn extract_exec_name_from_path(path: &Path) -> anyhow::Result<String> {
|
||||
path.file_stem()
|
||||
.and_then(|s| s.to_str())
|
||||
.map(String::from)
|
||||
.ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"couldn't extract the file stem from path {}",
|
||||
path.display()
|
||||
)
|
||||
})
|
||||
}
|
||||
380
crates/test-programs/wasi-http-tests/Cargo.lock
generated
Normal file
380
crates/test-programs/wasi-http-tests/Cargo.lock
generated
Normal file
@@ -0,0 +1,380 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "id-arena"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "leb128"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pulldown-cmark"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"memchr",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.158"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.158"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0da4a3c17e109f700685ec577c0f85efd9b19bcf15c913985f14dc1ac01775aa"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi-http-tests"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4eff853c4f09eec94d76af527eddad4e9de13b11d6286a1ef7134bc30135a2b7"
|
||||
dependencies = [
|
||||
"leb128",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-metadata"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd6956efd8a1a2c48a707e9a1b2da729834a0f8e4c58117493b0d9d089cee468"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
"serde",
|
||||
"wasm-encoder",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.102.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48134de3d7598219ab9eaf6b91b15d8e50d31da76b8519fe4ecfcec2cf35104b"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e7cf57f8786216c5652e1228b25203af2ff523808b5e9d3671894eee2bf7264"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"wit-bindgen-rust-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-core"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef177b73007d86c720931d0e2ea7e30eb8c9776e58361717743fc1e83cfacfe5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"wit-component",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efdf5b00935b7b52d0e56cae1960f8ac13019a285f5aa762ff6bd7139a5c28a2"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"wasm-metadata",
|
||||
"wit-bindgen-core",
|
||||
"wit-bindgen-rust-lib",
|
||||
"wit-component",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust-lib"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab0a8f4b5fb1820b9d232beb122936425f72ec8fe6acb56e5d8782cfe55083da"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"wit-bindgen-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust-macro"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cadf1adf12ed25629b06272c16b335ef8c5a240d0ca64ab508a955ac3b46172c"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"proc-macro2",
|
||||
"syn 1.0.109",
|
||||
"wit-bindgen-core",
|
||||
"wit-bindgen-rust",
|
||||
"wit-component",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-component"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed04310239706efc71cc8b995cb0226089c5b5fd260c3bd800a71486bd3cec97"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
"indexmap",
|
||||
"log",
|
||||
"url",
|
||||
"wasm-encoder",
|
||||
"wasm-metadata",
|
||||
"wasmparser",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-parser"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f887c3da527a51b321076ebe6a7513026a4757b6d4d144259946552d6fc728b3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"id-arena",
|
||||
"indexmap",
|
||||
"log",
|
||||
"pulldown-cmark",
|
||||
"unicode-xid",
|
||||
"url",
|
||||
]
|
||||
15
crates/test-programs/wasi-http-tests/Cargo.toml
Normal file
15
crates/test-programs/wasi-http-tests/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "wasi-http-tests"
|
||||
version = "0.0.0"
|
||||
readme = "README.md"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
wit-bindgen = "0.4.0"
|
||||
|
||||
# This crate is built with the wasm32-wasi target, so it's separate
|
||||
# from the main Wasmtime build, so use this directive to exclude it
|
||||
# from the parent directory's workspace.
|
||||
[workspace]
|
||||
132
crates/test-programs/wasi-http-tests/src/bin/outbound_request.rs
Normal file
132
crates/test-programs/wasi-http-tests/src/bin/outbound_request.rs
Normal file
@@ -0,0 +1,132 @@
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use std::fmt;
|
||||
use wasi_http_tests::*;
|
||||
|
||||
struct Response {
|
||||
status: types::StatusCode,
|
||||
headers: Vec<(String, String)>,
|
||||
body: Vec<u8>,
|
||||
}
|
||||
impl fmt::Debug for Response {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut out = f.debug_struct("Response");
|
||||
out.field("status", &self.status)
|
||||
.field("headers", &self.headers);
|
||||
if let Ok(body) = std::str::from_utf8(&self.body) {
|
||||
out.field("body", &body);
|
||||
} else {
|
||||
out.field("body", &self.body);
|
||||
}
|
||||
out.finish()
|
||||
}
|
||||
}
|
||||
|
||||
fn request(
|
||||
method: types::MethodParam<'_>,
|
||||
scheme: types::SchemeParam<'_>,
|
||||
authority: &str,
|
||||
path: &str,
|
||||
query: &str,
|
||||
body: &[u8],
|
||||
) -> Result<Response> {
|
||||
let headers = types::new_fields(&[
|
||||
("User-agent", "WASI-HTTP/0.0.1"),
|
||||
("Content-type", "application/json"),
|
||||
]);
|
||||
|
||||
let request =
|
||||
types::new_outgoing_request(method, path, query, Some(scheme), authority, headers);
|
||||
|
||||
let request_body = types::outgoing_request_write(request)
|
||||
.map_err(|_| anyhow!("outgoing request write failed"))?;
|
||||
|
||||
let mut body_cursor = 0;
|
||||
while body_cursor < body.len() {
|
||||
let written =
|
||||
streams::write(request_body, &body[body_cursor..]).context("writing request body")?;
|
||||
body_cursor += written as usize;
|
||||
}
|
||||
|
||||
streams::drop_output_stream(request_body);
|
||||
|
||||
let future_response = default_outgoing_http::handle(request, None);
|
||||
// TODO: we could create a pollable from the future_response and poll on it here to test that
|
||||
// its available immediately
|
||||
|
||||
types::drop_outgoing_request(request);
|
||||
|
||||
let incoming_response = types::future_incoming_response_get(future_response)
|
||||
.ok_or_else(|| anyhow!("incoming response is available immediately"))?
|
||||
// TODO: maybe anything that appears in the Result<_, E> position should impl
|
||||
// Error? anyway, just use its Debug here:
|
||||
.map_err(|e| anyhow!("incoming response error: {e:?}"))?;
|
||||
|
||||
types::drop_future_incoming_response(future_response);
|
||||
|
||||
let status = types::incoming_response_status(incoming_response);
|
||||
|
||||
let headers_handle = types::incoming_response_headers(incoming_response);
|
||||
let headers = types::fields_entries(headers_handle);
|
||||
types::drop_fields(headers_handle);
|
||||
|
||||
let body_stream = types::incoming_response_consume(incoming_response)
|
||||
.map_err(|()| anyhow!("incoming response has no body stream"))?;
|
||||
types::drop_incoming_response(incoming_response);
|
||||
|
||||
let mut body = Vec::new();
|
||||
let mut eof = false;
|
||||
while !eof {
|
||||
let (mut body_chunk, stream_ended) = streams::read(body_stream, u64::MAX)?;
|
||||
eof = stream_ended;
|
||||
body.append(&mut body_chunk);
|
||||
}
|
||||
streams::drop_input_stream(body_stream);
|
||||
|
||||
Ok(Response {
|
||||
status,
|
||||
headers,
|
||||
body,
|
||||
})
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let r1 = request(
|
||||
types::MethodParam::Get,
|
||||
types::SchemeParam::Http,
|
||||
"postman-echo.com",
|
||||
"/get",
|
||||
"?some=arg?goes=here",
|
||||
&[],
|
||||
)
|
||||
.context("postman-echo /get")?;
|
||||
|
||||
println!("postman-echo /get: {r1:?}");
|
||||
assert_eq!(r1.status, 200);
|
||||
|
||||
let r2 = request(
|
||||
types::MethodParam::Post,
|
||||
types::SchemeParam::Http,
|
||||
"postman-echo.com",
|
||||
"/post",
|
||||
"",
|
||||
b"{\"foo\": \"bar\"}",
|
||||
)
|
||||
.context("postman-echo /post")?;
|
||||
|
||||
println!("postman-echo /post: {r2:?}");
|
||||
assert_eq!(r2.status, 200);
|
||||
|
||||
let r3 = request(
|
||||
types::MethodParam::Put,
|
||||
types::SchemeParam::Http,
|
||||
"postman-echo.com",
|
||||
"/put",
|
||||
"",
|
||||
&[],
|
||||
)
|
||||
.context("postman-echo /put")?;
|
||||
|
||||
println!("postman-echo /put: {r3:?}");
|
||||
assert_eq!(r3.status, 200);
|
||||
Ok(())
|
||||
}
|
||||
3
crates/test-programs/wasi-http-tests/src/lib.rs
Normal file
3
crates/test-programs/wasi-http-tests/src/lib.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
// The macro will generate a macro for defining exports which we won't be reusing
|
||||
#![allow(unused)]
|
||||
wit_bindgen::generate!({ path: "../../wasi-http/wasi-http/wit" });
|
||||
Reference in New Issue
Block a user