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:
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