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:
Brendan Burns
2023-04-05 13:33:03 -07:00
committed by GitHub
parent 7eb8914090
commit 2d34dbef4b
26 changed files with 2284 additions and 123 deletions

View 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(())
}

View 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" });