Skip to content

Commit

Permalink
Merge pull request #20 from fermyon/helper-crate
Browse files Browse the repository at this point in the history
Helper crate
  • Loading branch information
rylev committed Jun 26, 2024
2 parents 5aa8463 + 35840cb commit db0ee32
Show file tree
Hide file tree
Showing 18 changed files with 203 additions and 271 deletions.
16 changes: 16 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ resolver = "2"

[workspace.dependencies]
anyhow = "1.0"
helper = { path = "crates/helper" }
wit-bindgen = "0.26"
3 changes: 2 additions & 1 deletion components/key-value/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ edition = "2021"
crate-type = ["cdylib"]

[dependencies]
wit-bindgen = { workspace = true }
anyhow = { workspace = true }
helper = { workspace = true }
wit-bindgen = { workspace = true }
60 changes: 13 additions & 47 deletions components/key-value/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,41 +1,23 @@
use anyhow::Context;
use bindings::{
exports::wasi::http0_2_0::incoming_handler::Guest,
fermyon::spin2_0_0::key_value::{Error, Store},
wasi::http0_2_0::types::{
ErrorCode, Headers, IncomingRequest, OutgoingBody, OutgoingResponse, ResponseOutparam,
},
};

mod bindings {
wit_bindgen::generate!({
world: "http-trigger",
path: "../../wit",
});
use super::Component;
export!(Component);
}
use anyhow::Context as _;

struct Component;

impl Guest for Component {
helper::gen_http_trigger_bindings!(Component);

use helper::bindings::{
fermyon::spin2_0_0::key_value::{Error, Store},
wasi::http0_2_0::types::{IncomingRequest, OutgoingResponse, ResponseOutparam},
};

impl bindings::Guest for Component {
fn handle(request: IncomingRequest, response_out: ResponseOutparam) {
let result = handle(request)
.map(|r| match r {
Err(e) => response(500, format!("{e}").as_bytes()),
Ok(()) => response(200, b""),
})
.map_err(|e| ErrorCode::InternalError(Some(e.to_string())));
ResponseOutparam::set(response_out, result)
helper::handle_result(handle(request), response_out);
}
}

fn handle(_req: IncomingRequest) -> anyhow::Result<Result<(), Error>> {
fn handle(_req: IncomingRequest) -> anyhow::Result<OutgoingResponse> {
anyhow::ensure!(matches!(Store::open("forbidden"), Err(Error::AccessDenied)));
let store = match Store::open("default") {
Ok(s) => s,
Err(e) => return Ok(Err(e)),
};
let store = Store::open("default")?;

// Ensure nothing set in `bar` key
store.delete("bar").context("could not delete 'bar' key")?;
Expand Down Expand Up @@ -71,21 +53,5 @@ fn handle(_req: IncomingRequest) -> anyhow::Result<Result<(), Error>> {
anyhow::ensure!(matches!(store.get("qux"), Ok(None)));
anyhow::ensure!(matches!(store.get_keys().as_deref(), Ok(&[])));

Ok(Ok(()))
}

fn response(status: u16, body: &[u8]) -> OutgoingResponse {
let response = OutgoingResponse::new(Headers::new());
response.set_status_code(status).unwrap();
if !body.is_empty() {
assert!(body.len() <= 4096);
let outgoing_body = response.body().unwrap();
{
let outgoing_stream = outgoing_body.write().unwrap();
outgoing_stream.blocking_write_and_flush(body).unwrap();
// The outgoing stream must be dropped before the outgoing body is finished.
}
OutgoingBody::finish(outgoing_body, None).unwrap();
}
response
Ok(helper::ok_response())
}
1 change: 1 addition & 0 deletions components/outbound-redis/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ crate-type = ["cdylib"]

[dependencies]
anyhow = { workspace = true }
helper = { workspace = true }
wit-bindgen = { workspace = true }
59 changes: 14 additions & 45 deletions components/outbound-redis/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,44 +1,29 @@
use anyhow::Context as _;
use bindings::{
exports::wasi::http0_2_0::incoming_handler::Guest,
fermyon::spin2_0_0::redis,
wasi::http0_2_0::types::{
Headers, IncomingRequest, OutgoingBody, OutgoingResponse, ResponseOutparam,
},
};

struct Component;

mod bindings {
wit_bindgen::generate!({
world: "http-trigger",
path: "../../wit",
});
use super::Component;
export!(Component);
}
helper::gen_http_trigger_bindings!(Component);

impl Guest for Component {
use helper::bindings::{
fermyon::spin2_0_0::redis,
wasi::http0_2_0::types::{IncomingRequest, OutgoingResponse, ResponseOutparam},
};

impl bindings::Guest for Component {
fn handle(request: IncomingRequest, response_out: ResponseOutparam) {
let result = match handle(request) {
Err(e) => response(500, format!("{e}").as_bytes()),
Ok(r) => r,
};
ResponseOutparam::set(response_out, Ok(result))
helper::handle_result(handle(request), response_out);
}
}

const REDIS_ADDRESS_HEADER: &str = "REDIS_ADDRESS";

fn handle(request: IncomingRequest) -> anyhow::Result<OutgoingResponse> {
let Some(address) = request
.headers()
.get(&REDIS_ADDRESS_HEADER.to_owned())
.pop()
.and_then(|v| String::from_utf8(v).ok())
else {
let Some(address) = helper::get_header(request, &REDIS_ADDRESS_HEADER.to_owned()) else {
// Otherwise, return a 400 Bad Request response.
return Ok(response(400, b"Bad Request"));
return Ok(helper::response(
400,
format!("missing header: {REDIS_ADDRESS_HEADER}").as_bytes(),
));
};
let connection = redis::Connection::open(&address)?;

Expand Down Expand Up @@ -87,21 +72,5 @@ fn handle(request: IncomingRequest) -> anyhow::Result<OutgoingResponse> {
values.as_slice(),
&[redis::RedisResult::Binary(ref b)] if b == b"Eureka! I've got it!"));

Ok(response(200, b""))
}

fn response(status: u16, body: &[u8]) -> OutgoingResponse {
let response = OutgoingResponse::new(Headers::new());
response.set_status_code(status).unwrap();
if !body.is_empty() {
assert!(body.len() <= 4096);
let outgoing_body = response.body().unwrap();
{
let outgoing_stream = outgoing_body.write().unwrap();
outgoing_stream.blocking_write_and_flush(body).unwrap();
// The outgoing stream must be dropped before the outgoing body is finished.
}
OutgoingBody::finish(outgoing_body, None).unwrap();
}
response
Ok(helper::ok_response())
}
2 changes: 2 additions & 0 deletions components/outbound-wasi-http-v0.2.0/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@ edition = "2021"
crate-type = ["cdylib"]

[dependencies]
anyhow = { workspace = true }
helper = { workspace = true }
url = "2.4.0"
wit-bindgen = { workspace = true }
45 changes: 11 additions & 34 deletions components/outbound-wasi-http-v0.2.0/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,16 @@
wit_bindgen::generate!({
path: "../../wit",
world: "wasi:http/proxy@0.2.0",
});

use exports::wasi::http0_2_0::incoming_handler;
use url::Url;
use wasi::{
http0_2_0::{
outgoing_handler,
types::{
Headers, IncomingRequest, Method, OutgoingBody, OutgoingRequest, OutgoingResponse,
ResponseOutparam, Scheme,
},
},
io0_2_0::streams::StreamError,
use helper::bindings::wasi::http0_2_0::outgoing_handler;
use helper::bindings::wasi::http0_2_0::types::{
Headers, IncomingRequest, Method, OutgoingBody, OutgoingRequest, OutgoingResponse,
ResponseOutparam, Scheme,
};
use helper::bindings::wasi::io0_2_0::streams::StreamError;
use url::Url;

struct Component;

export!(Component);
helper::gen_http_trigger_bindings!(Component);

impl incoming_handler::Guest for Component {
impl bindings::Guest for Component {
fn handle(request: IncomingRequest, outparam: ResponseOutparam) {
// The request must have a "url" header.
let Some(url) = request.headers().entries().iter().find_map(|(k, v)| {
Expand All @@ -30,7 +20,7 @@ impl incoming_handler::Guest for Component {
.and_then(|v| Url::parse(v).ok())
}) else {
// Otherwise, return a 400 Bad Request response.
return_response(outparam, 400, b"Bad Request");
return_response(outparam, 400, b"missing header: url");
return;
};

Expand All @@ -55,7 +45,7 @@ impl incoming_handler::Guest for Component {
.unwrap();
// Write the request body.
write_outgoing_body(outgoing_request.body().unwrap(), b"Hello, world!");
helper::write_outgoing_body(outgoing_request.body().unwrap(), b"Hello, world!");

// Get the incoming response.
let response = match outgoing_handler::handle(outgoing_request, None) {
Expand Down Expand Up @@ -103,20 +93,7 @@ impl incoming_handler::Guest for Component {
}
}

fn write_outgoing_body(outgoing_body: OutgoingBody, message: &[u8]) {
assert!(message.len() <= 4096);
{
let outgoing_stream = outgoing_body.write().unwrap();
outgoing_stream.blocking_write_and_flush(message).unwrap();
// The outgoing stream must be dropped before the outgoing body is finished.
}
OutgoingBody::finish(outgoing_body, None).unwrap();
}

fn return_response(outparam: ResponseOutparam, status: u16, body: &[u8]) {
let response = OutgoingResponse::new(Headers::new());
response.set_status_code(status).unwrap();
write_outgoing_body(response.body().unwrap(), body);

let response = helper::response(status, body);
ResponseOutparam::set(outparam, Ok(response));
}
3 changes: 2 additions & 1 deletion components/request-shape/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ edition = "2021"
crate-type = ["cdylib"]

[dependencies]
wit-bindgen = { workspace = true }
anyhow = { workspace = true }
helper = { workspace = true }
wit-bindgen = { workspace = true }
30 changes: 8 additions & 22 deletions components/request-shape/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,24 @@
use anyhow::Context;
use anyhow::Context as _;
use std::collections::HashMap;

mod bindings {
wit_bindgen::generate!({
world: "http-trigger",
path: "../../wit",
});
use super::Component;
export!(Component);
}

use bindings::{
exports::wasi::http0_2_0::incoming_handler::{Guest, IncomingRequest, ResponseOutparam},
wasi::http0_2_0::types::{ErrorCode, Headers, OutgoingResponse},
use helper::bindings::wasi::http0_2_0::types::{
IncomingRequest, Method, OutgoingResponse, ResponseOutparam, Scheme,
};

use crate::bindings::wasi::http0_2_0::types::{Method, Scheme};

pub struct Component;
helper::gen_http_trigger_bindings!(Component);

impl Guest for Component {
impl bindings::Guest for Component {
fn handle(request: IncomingRequest, response_out: ResponseOutparam) {
let result = handle(request)
.map(|_| OutgoingResponse::new(Headers::new()))
.map_err(|e| ErrorCode::InternalError(Some(e.to_string())));
ResponseOutparam::set(response_out, result)
helper::handle_result(handle(request), response_out);
}
}

fn handle(req: IncomingRequest) -> anyhow::Result<()> {
fn handle(req: IncomingRequest) -> anyhow::Result<OutgoingResponse> {
check_method(&req)?;
check_url(&req)?;
check_headers(&req)?;
Ok(())
Ok(helper::ok_response())
}

fn check_method(req: &IncomingRequest) -> anyhow::Result<()> {
Expand Down
3 changes: 2 additions & 1 deletion components/sqlite/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ version = "0.1.0"
edition = "2021"

[dependencies]
wit-bindgen = { workspace = true }
anyhow = { workspace = true }
helper = { workspace = true }
wit-bindgen = { workspace = true }

[lib]
crate-type = ["cdylib"]
Loading

0 comments on commit db0ee32

Please sign in to comment.