Skip to content

Commit

Permalink
feat(http-server): Add WASI reactor support to http-server cap (#359)
Browse files Browse the repository at this point in the history
  • Loading branch information
Mossaka authored Mar 15, 2023
1 parent 5e71d2b commit 74f0009
Show file tree
Hide file tree
Showing 23 changed files with 702 additions and 607 deletions.
980 changes: 461 additions & 519 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -99,5 +99,7 @@ rand = "0.8"
members = [
"crates/core",
"crates/common",
"crates/http-server-macro",
"crates/http-handler-macro",
"tests"
]
7 changes: 5 additions & 2 deletions crates/common/src/context.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use slight_http_api::HttpHandlerData;
use slight_http_api::{HttpHandlerData, HttpServerExportData};

/// A WebAssembly runtime context to be consumed by the wasm component.
pub trait Ctx {
/// Get the mutable reference to the http handler data.
fn get_http_state_mut(&mut self) -> &mut HttpHandlerData;
fn get_http_handler_mut(&mut self) -> &mut HttpHandlerData;

/// Get the mutable reference to the http server data.
fn get_http_server_mut(&mut self) -> &mut HttpServerExportData;

/// Get the runtime host state for a given resource key.
fn get_host_state<T: 'static, TTable: 'static>(
Expand Down
39 changes: 39 additions & 0 deletions crates/http-api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,53 @@
use anyhow::{bail, Result};
pub use http_handler::{HttpError, HttpHandlerData, Method, Request, Response};
pub use http_server_export::HttpServerExportData;
use hyper::{
body::HttpBody as HyperHttpBody,
header::{HeaderName, HeaderValue},
Body, HeaderMap, StatusCode,
};

wit_bindgen_wasmtime::import!({paths: ["../../wit/http-handler.wit"], async: *});
wit_bindgen_wasmtime::import!({paths: ["../../wit/http-server-export.wit"], async: *});
wit_error_rs::impl_error!(http_handler::HttpError);

/// An exported HTTP server init function from the wasm module
///
/// This is a wrapper implementation of the WIT generated `HttpServerExport`
pub struct HttpServerInit<T> {
inner: http_server_export::HttpServerExport<T>,
}

impl<T> AsRef<http_server_export::HttpServerExport<T>> for HttpServerInit<T> {
fn as_ref(&self) -> &http_server_export::HttpServerExport<T> {
&self.inner
}
}

impl<T> AsMut<http_server_export::HttpServerExport<T>> for HttpServerInit<T> {
fn as_mut(&mut self) -> &mut http_server_export::HttpServerExport<T> {
&mut self.inner
}
}

impl<T: Send> HttpServerInit<T> {
pub fn new(
store: impl wasmtime::AsContextMut<Data = T>,
instance: &wasmtime::Instance,
get_state: impl Fn(&mut T) -> &mut HttpServerExportData + Send + Sync + Copy + 'static,
) -> Result<Self> {
http_server_export::HttpServerExport::new(store, instance, get_state)
.map(|inner| Self { inner })
}

pub async fn on_server_init(
&self,
caller: impl wasmtime::AsContextMut<Data = T>,
) -> Result<Result<(), String>, anyhow::Error> {
self.inner.on_server_init(caller).await
}
}

/// A HTTP Handler that finds the handler function from the wasm module
/// and calls it with the HTTP request.
///
Expand Down
22 changes: 14 additions & 8 deletions crates/http-handler-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ pub fn register_handler(_attr: TokenStream, item: TokenStream) -> TokenStream {
// builds struct name from function name
let struct_name = handle_func
.split('_')
.into_iter()
.map(capitalize_first_letter)
.collect::<String>();

Expand All @@ -74,7 +73,6 @@ pub fn register_handler(_attr: TokenStream, item: TokenStream) -> TokenStream {
// builds trait name from mod name
let trait_name = mod_name
.split('_')
.into_iter()
.map(capitalize_first_letter)
.collect::<String>();
let internal_mod = format!("{}_internal", &mod_name);
Expand Down Expand Up @@ -120,6 +118,20 @@ pub fn register_handler(_attr: TokenStream, item: TokenStream) -> TokenStream {
impl #trait_ident for #struct_ident {
#func
}

impl std::fmt::Display for #mod_ident::Method {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
#mod_ident::Method::Get => write!(f, "GET"),
#mod_ident::Method::Post => write!(f, "POST"),
#mod_ident::Method::Put => write!(f, "PUT"),
#mod_ident::Method::Delete => write!(f, "DELETE"),
#mod_ident::Method::Patch => write!(f, "PATCH"),
#mod_ident::Method::Head => write!(f, "HEAD"),
#mod_ident::Method::Options => write!(f, "OPTIONS")
}
}
}
}
)
.into()
Expand All @@ -135,15 +147,13 @@ mod unittests {
let struct_name = func_name
.to_string()
.split('_')
.into_iter()
.map(capitalize_first_letter)
.collect::<String>();
assert_eq!(struct_name, "HandleHello".to_string());

let mod_name = format!("{func_name}_mod");
let trait_name = mod_name
.split('_')
.into_iter()
.map(capitalize_first_letter)
.collect::<String>();
assert_eq!(trait_name, "HandleHelloMod");
Expand All @@ -152,15 +162,13 @@ mod unittests {
let struct_name = func_name
.to_string()
.split('_')
.into_iter()
.map(capitalize_first_letter)
.collect::<String>();
assert_eq!(struct_name, "Handle".to_string());

let mod_name = format!("{func_name}_mod");
let trait_name = mod_name
.split('_')
.into_iter()
.map(capitalize_first_letter)
.collect::<String>();
assert_eq!(trait_name, "HandleMod");
Expand All @@ -169,15 +177,13 @@ mod unittests {
let struct_name = func_name
.to_string()
.split('_')
.into_iter()
.map(capitalize_first_letter)
.collect::<String>();
assert_eq!(struct_name, "HandleFuncA".to_string());

let mod_name = format!("{func_name}_mod");
let trait_name = mod_name
.split('_')
.into_iter()
.map(capitalize_first_letter)
.collect::<String>();
assert_eq!(trait_name, "HandleFuncAMod");
Expand Down
19 changes: 19 additions & 0 deletions crates/http-server-macro/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "slight-http-server-macro"
version = "0.1.0"
edition = { workspace = true }
authors = { workspace = true }
license = { workspace = true }
repository = { workspace = true }

[lib]
proc-macro = true
doctest = false

[dependencies]
anyhow = { workspace = true }
proc-macro2 = "1"
quote = "1"
syn = { version = "1", features = ["full"] }
wit-bindgen-gen-rust-wasm = { git = "https://github.com/fermyon/wit-bindgen-backport" }
wit-bindgen-gen-core = { git = "https://github.com/fermyon/wit-bindgen-backport" }
28 changes: 28 additions & 0 deletions crates/http-server-macro/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use proc_macro::TokenStream;
use quote::quote;

#[proc_macro_attribute]
pub fn on_server_init(_attr: TokenStream, item: TokenStream) -> TokenStream {
// parse the item as rust Fn
let func = syn::parse_macro_input!(item as syn::ItemFn);
let func_name = &func.sig.ident;

// generate rust code
quote!(

struct HttpServerExport {}

impl http_server_export::HttpServerExport for HttpServerExport {
fn on_server_init() -> Result<(), String> {

#func

match #func_name() {
Ok(_) => Ok(()),
Err(e) => Err(e.to_string()),
}
}
}
)
.into()
}
3 changes: 2 additions & 1 deletion crates/http-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use slight_common::{impl_resource, Builder, Ctx, WasmtimeBuildable};
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
use tracing::log;

pub use slight_http_api::HttpServerInit;
use slight_http_api::{HttpBody, HttpHandler, HttpHeader, Method, Request};

wit_bindgen_wasmtime::export!("../../wit/http-server.wit");
Expand Down Expand Up @@ -335,7 +336,7 @@ async fn handler<T: WasmtimeBuildable + Send + Sync + 'static>(
// Construct http handler
let handler_name = &route.handler.replace('_', "-");
let handler = HttpHandler::new(&mut store, &instance, handler_name, |ctx| {
ctx.get_http_state_mut()
ctx.get_http_handler_mut()
})?;

// Invoke the handler with http request
Expand Down
10 changes: 8 additions & 2 deletions crates/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::{
use anyhow::Result;
use async_trait::async_trait;
use ctx::SlightCtxBuilder;
use resource::{get_host_state, HttpData};
use resource::{get_host_state, HttpData, HttpServerExportData};
use slight_common::{CapabilityBuilder, WasmtimeBuildable, WasmtimeLinkable};
use wasi_cap_std_sync::{ambient_authority, Dir, WasiCtxBuilder};
use wasi_common::pipe::{ReadPipe, WritePipe};
Expand All @@ -34,10 +34,11 @@ pub struct RuntimeContext {
pub wasi: Option<WasiCtx>,
pub slight: SlightCtx,
pub http_state: HttpData,
pub http_server_state: HttpServerExportData,
}

impl slight_common::Ctx for RuntimeContext {
fn get_http_state_mut(&mut self) -> &mut HttpData {
fn get_http_handler_mut(&mut self) -> &mut HttpData {
&mut self.http_state
}

Expand All @@ -47,6 +48,10 @@ impl slight_common::Ctx for RuntimeContext {
) -> (&mut T, &mut TTable) {
get_host_state(self, resource_key)
}

fn get_http_server_mut(&mut self) -> &mut slight_http_api::HttpServerExportData {
&mut self.http_server_state
}
}

/// Input and output redirects to be used for the running module
Expand Down Expand Up @@ -147,6 +152,7 @@ impl WasmtimeBuildable for Builder {
wasi: Some(wasi),
slight: self.state_builder.build(),
http_state: HttpData::default(),
http_server_state: HttpServerExportData::default(),
};

let mut store = Store::new(&self.engine, ctx);
Expand Down
2 changes: 1 addition & 1 deletion crates/runtime/src/resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pub use crate::RuntimeContext;

use as_any::Downcast;

use slight_http_api::HttpHandlerData;
pub use slight_http_api::{HttpHandlerData, HttpServerExportData};
pub use wasmtime::Linker;

/// Guest data for http handler
Expand Down
Loading

0 comments on commit 74f0009

Please sign in to comment.