Skip to content

Commit

Permalink
feat: added http-client capability and a reqwest implementation (#285)
Browse files Browse the repository at this point in the history
* added http-outbound capability

Signed-off-by: Jiaxiao Zhou <[email protected]>
  • Loading branch information
Mossaka authored Jan 5, 2023
1 parent be069b7 commit e30b310
Show file tree
Hide file tree
Showing 31 changed files with 608 additions and 131 deletions.
45 changes: 29 additions & 16 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ members = [
"slight",
"crates/keyvalue",
"crates/distributed-locking",
"crates/http",
"crates/http-server",
"crates/http-client",
"crates/http-api",
"crates/http-handler-macro",
"crates/messaging",
Expand Down
15 changes: 9 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ build-rust:
cargo build --target wasm32-wasi --release --manifest-path ./examples/distributed-locking-demo/Cargo.toml & \
cargo build --target wasm32-wasi --release --manifest-path ./examples/messaging-producer-demo/Cargo.toml & \
cargo build --target wasm32-wasi --release --manifest-path ./examples/messaging-consumer-demo/Cargo.toml & \
cargo build --target wasm32-wasi --release --manifest-path ./examples/http-demo/Cargo.toml & \
cargo build --target wasm32-wasi --release --manifest-path ./examples/http-server-demo/Cargo.toml & \
cargo build --target wasm32-wasi --release --manifest-path ./examples/http-client-demo/Cargo.toml & \
wait; \
/bin/sh -c 'echo "DONE"'

Expand Down Expand Up @@ -126,11 +127,13 @@ run-rust:
.PHONY: clean-rust
clean-rust:
cargo clean --manifest-path ./examples/configs-demo/Cargo.toml & \
cargo clean --manifest-path ./examples/multi_capability-demo/Cargo.toml & \
cargo clean --manifest-path ./examples/keyvalue-demo/Cargo.toml & \
cargo clean --manifest-path ./examples/distributed-locking-demo/Cargo.toml & \
cargo clean --manifest-path ./examples/messaging-producer-demo/Cargo.toml & \
cargo clean --manifest-path ./examples/messaging-consumer-demo/Cargo.toml & \
cargo clean --manifest-path ./examples/multi_capability-demo/Cargo.toml & \
cargo clean --manifest-path ./examples/keyvalue-demo/Cargo.toml & \
cargo clean --manifest-path ./examples/distributed-locking-demo/Cargo.toml & \
cargo clean --manifest-path ./examples/messaging-producer-demo/Cargo.toml & \
cargo clean --manifest-path ./examples/messaging-consumer-demo/Cargo.toml & \
cargo clean --manifest-path ./examples/http-client-demo/Cargo.toml & \
cargo clean --manifest-path ./examples/http-server-demo/Cargo.toml & \
wait; \
/bin/sh -c 'echo "DONE"'

Expand Down
35 changes: 28 additions & 7 deletions crates/common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ pub trait Capability: AsAny {}
/// A trait for wit-bindgen resource tables. see [here](https://github.com/bytecodealliance/wit-bindgen/blob/main/crates/wasmtime/src/table.rs) for more details:
pub trait CapabilityIndexTable: AsAny {}

impl CapabilityIndexTable for () {}

pub trait CapabilityBuilder {
fn build(self) -> Result<HostState>;
}
Expand All @@ -33,14 +35,34 @@ pub type HostState = (
#[allow(unknown_lints)]
#[allow(clippy::crate_in_macro_def)]
macro_rules! impl_resource {
($resource:ident, $resource_table:ty, $state:ident, $add_to_linker:path, $scheme_name:expr) => {
($resource:ident, $add_to_linker:path, $scheme_name:expr) => {
// This macro is used to the implement a new Capability. It does not
// require a resource table.
impl slight_common::Capability for $resource {}
impl slight_common::CapabilityBuilder for $resource {
fn build(self) -> anyhow::Result<slight_common::HostState> {
Ok((Box::new(self), Some(Box::new(()))))
}
}

impl slight_common::WasmtimeLinkable for $resource {
fn add_to_linker<Ctx: slight_common::Ctx + Send + Sync + 'static>(
linker: &mut slight_common::Linker<Ctx>,
) -> anyhow::Result<()> {
$add_to_linker(linker, |ctx| {
Ctx::get_host_state::<$resource, ()>(ctx, $scheme_name).0
})
}
}
};

($resource:ident, $resource_table:ty, $add_to_linker:path, $scheme_name:expr) => {
// This macro is used to the implement a new Capability. It requires a
// resource table.
impl slight_common::Capability for $resource {}
impl slight_common::CapabilityIndexTable for $resource_table {}
impl slight_common::CapabilityBuilder for $resource {
fn build(self) -> anyhow::Result<slight_common::HostState> {
/// We prepare a default resource with host-provided state.
/// Then the guest will pass other configuration state to the resource.
/// This is done in the `<Capability>::open` function.
Ok((Box::new(self), Some(Box::new(<$resource_table>::default()))))
}
}
Expand All @@ -57,6 +79,8 @@ macro_rules! impl_resource {
};

($resource:ty, $resource_table:ty, $state:ty, $lt:tt, $add_to_linker:path, $scheme_name:expr) => {
// This macro is used to the implement a new Capability. It requires
// a resource table and is generic to builder type.
impl<$lt> slight_common::Capability for $resource where
$lt: slight_common::WasmtimeBuildable + 'static
{
Expand All @@ -70,9 +94,6 @@ macro_rules! impl_resource {
$lt: slight_common::WasmtimeBuildable + Send + Sync + 'static,
{
fn build(self) -> anyhow::Result<slight_common::HostState> {
/// We prepare a default resource with host-provided state.
/// Then the guest will pass other configuration state to the resource.
/// This is done in the `<Capability>::open` function.
Ok((Box::new(self), Some(Box::new(<$resource_table>::default()))))
}
}
Expand Down
1 change: 0 additions & 1 deletion crates/distributed-locking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ impl DistributedLocking {
impl_resource!(
DistributedLocking,
distributed_locking::DistributedLockingTables<DistributedLocking>,
DistributedLockingState,
distributed_locking::add_to_linker,
"distributed_locking".to_string()
);
Expand Down
18 changes: 18 additions & 0 deletions crates/http-client/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "slight-http-client"
version = "0.1.0"
edition = { workspace = true }
authors = { workspace = true }
license = { workspace = true }
repository = { workspace = true }

[lib]
doctest = false

[dependencies]
anyhow = { workspace = true }
async-trait = { workspace = true }
wit-bindgen-wasmtime = { workspace = true }
wit-error-rs = { workspace = true }
slight-common = { path = "../common" }
reqwest = { version = "0.11" }
74 changes: 74 additions & 0 deletions crates/http-client/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use anyhow::Result;
use async_trait::async_trait;
use reqwest::Client;

use http_client::*;
use slight_common::impl_resource;
wit_bindgen_wasmtime::export!({paths: ["../../wit/http-client.wit"], async: *});
wit_error_rs::impl_error!(http_client::HttpError);
wit_error_rs::impl_from!(anyhow::Error, http_client::HttpError::UnexpectedError);

#[derive(Clone, Default)]
pub struct HttpClient {
client: Client,
}

impl HttpClient {
pub fn new() -> Self {
Self {
client: Client::new(),
}
}
}

#[async_trait]
impl http_client::HttpClient for HttpClient {
async fn request(&mut self, req: Request<'_>) -> Result<Response, HttpError> {
let mut builder = self.client.request(req.method.into(), req.uri);
for header in req.headers {
builder = builder.header(header.0, header.1);
}
let res = builder.send().await?;

let status = res.status().as_u16();
let mut headers = vec![];
for (name, value) in res.headers().iter() {
headers.push((
name.as_str().to_string(),
value.to_str().unwrap().to_string(),
));
}
let body = Some(res.bytes().await?.to_vec());
Ok(Response {
status,
headers: Some(headers),
body,
})
}
}

impl_resource!(
HttpClient,
http_client::add_to_linker,
"http-client".to_string()
);

impl From<http_client::Method> for reqwest::Method {
fn from(method: http_client::Method) -> Self {
match method {
http_client::Method::Get => reqwest::Method::GET,
http_client::Method::Post => reqwest::Method::POST,
http_client::Method::Put => reqwest::Method::PUT,
http_client::Method::Delete => reqwest::Method::DELETE,
http_client::Method::Head => reqwest::Method::HEAD,
http_client::Method::Options => reqwest::Method::OPTIONS,
http_client::Method::Patch => reqwest::Method::PATCH,
}
}
}

impl From<reqwest::Error> for http_client::HttpError {
fn from(e: reqwest::Error) -> Self {
Self::UnexpectedError(e.to_string())
}
}
2 changes: 1 addition & 1 deletion crates/http/Cargo.toml → crates/http-server/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "slight-http"
name = "slight-http-server"
version = "0.1.0"
edition = { workspace = true }
authors = { workspace = true }
Expand Down
Loading

0 comments on commit e30b310

Please sign in to comment.