Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(http-server): Add WASI reactor support to http-server cap #359

Merged
merged 7 commits into from
Mar 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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