Skip to content

Commit 07a55c6

Browse files
author
Peter Huene
authored
wasm-compose: update the example for latest tooling. (#941)
This commit updates the `wasm-compose` example to the latest tooling. It updates the examples for the latest `cargo-component` and updates Wasmtime and dependencies to latest. The server also has been updated to use the `bindgen` macro from Wasmtime for generating the server's bindings. Fixes #937.
1 parent 4ee705f commit 07a55c6

File tree

11 files changed

+100
-119
lines changed

11 files changed

+100
-119
lines changed

crates/wasm-compose/example/README.md

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,31 +19,33 @@ The server will listen for `POST` requests at `http://localhost:8080`.
1919
When it receives a request, the server will instantiate a service component
2020
and forward it the request.
2121

22-
Each component implements a `service` interface defined in `service.wit` as:
22+
Each service implements a `handler` interface defined in `service.wit` as:
2323

2424
```wit
25-
record request {
26-
headers: list<tuple<list<u8>, list<u8>>>,
27-
body: list<u8>,
25+
interface handler {
26+
record request {
27+
headers: list<tuple<list<u8>, list<u8>>>,
28+
body: list<u8>,
29+
}
30+
31+
record response {
32+
headers: list<tuple<list<u8>, list<u8>>>,
33+
body: list<u8>
34+
}
35+
36+
enum error {
37+
bad-request,
38+
}
39+
40+
execute: func(req: request) -> result<response, error>
2841
}
29-
30-
record response {
31-
headers: list<tuple<list<u8>, list<u8>>>,
32-
body: list<u8>
33-
}
34-
35-
enum error {
36-
bad-request,
37-
}
38-
39-
execute: func(req: request) -> expected<response, error>
4042
```
4143

42-
A service will be passed a `request` containing only the headers and body and
43-
respond with a `response` containing only the headers and body.
44+
A service handler will be passed a `request` containing only the headers and
45+
body and respond with a `response` containing only the headers and body.
4446

45-
Note that this is an overly-simplistic (and inefficient) interface for describing
46-
HTTP request processing.
47+
Note that this is an overly-simplistic (and inefficient) interface for
48+
describing HTTP request processing.
4749

4850
### Execution flow
4951

@@ -154,9 +156,9 @@ compressed.
154156

155157
## Composing with a middleware
156158

157-
If we want to instead compress the response bodies for the service, we can easily
158-
compose a new component that sends requests through the `middleware` component
159-
without rebuilding any of the previously built components.
159+
If we want to instead compress the response bodies for the service, we can
160+
easily compose a new component that sends requests through the `middleware`
161+
component without rebuilding any of the previously built components.
160162

161163
The `server/config.yml` file contains the configuration needed to compose a new
162164
component from the `service` and `middleware` components.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# This file is automatically generated by cargo-component.
2+
# It is not intended for manual editing.
3+
version = 1

crates/wasm-compose/example/middleware/Cargo.toml

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,12 @@ publish = false
66

77
[dependencies]
88
flate2 = "1.0.24"
9-
wit-bindgen-guest-rust = { git = "https://github.com/bytecodealliance/wit-bindgen", default_features = false }
9+
wit-bindgen = { version = "0.3.0", default_features = false }
1010

1111
[lib]
1212
crate-type = ["cdylib"]
1313

1414
[package.metadata.component]
15-
direct-export = "service"
16-
17-
[package.metadata.component.imports]
18-
backend = "../service.wit"
19-
20-
[package.metadata.component.exports]
21-
service = "../service.wit"
15+
target = { path = "../service.wit", world = "service.middleware" }
2216

2317
[workspace]

crates/wasm-compose/example/middleware/src/lib.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
use bindings::{
2-
backend,
3-
service::{Error, Request, Response, Service},
2+
downstream,
3+
handler::{Error, Handler, Request, Response},
44
};
55
use flate2::{write::GzEncoder, Compression};
66
use std::io::Write;
77

88
struct Component;
99

10-
impl Service for Component {
10+
impl Handler for Component {
1111
fn execute(req: Request) -> Result<Response, Error> {
1212
let headers: Vec<_> = req
1313
.headers
1414
.iter()
1515
.map(|(k, v)| (k.as_slice(), v.as_slice()))
1616
.collect();
1717

18-
// Send the request to the backend
19-
let mut response = backend::execute(backend::Request {
18+
// Send the request to the downstream service
19+
let mut response = downstream::execute(downstream::Request {
2020
headers: &headers,
2121
body: &req.body,
2222
})
@@ -25,7 +25,7 @@ impl Service for Component {
2525
body: r.body,
2626
})
2727
.map_err(|e| match e {
28-
backend::Error::BadRequest => Error::BadRequest,
28+
downstream::Error::BadRequest => Error::BadRequest,
2929
})?;
3030

3131
// If the response is already encoded, leave it alone

crates/wasm-compose/example/server/Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ publish = false
66

77
[dependencies]
88
async-std = { version = "1.12.0", features = ["attributes"] }
9-
clap = { version = "3.2.16", features = ["derive"] }
9+
clap = { version = "3.2.23", features = ["derive"] }
1010
driftwood = "0.0.6"
1111
tide = "0.16.0"
12-
wasmtime = { git = "https://github.com/bytecodealliance/wasmtime", rev = "b61e678", features = ["component-model"] }
12+
wasmtime = { version = "6.0.0", features = ["component-model"] }
13+
wasmtime-component-macro = "6.0.0"
1314

1415
[workspace]

crates/wasm-compose/example/server/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ search-paths:
44
instantiations:
55
$input:
66
arguments:
7-
backend: svc
7+
downstream: svc

crates/wasm-compose/example/server/src/main.rs

Lines changed: 31 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ use tide::{
1010
};
1111
use wasmtime::{component::*, Config, Engine, Store};
1212

13+
wasmtime_component_macro::bindgen!({
14+
path: "../service.wit",
15+
world: "service"
16+
});
17+
1318
/// Represents state stored in the tide application context.
1419
///
1520
/// This is so that a component is only parsed and compiled once.
@@ -33,42 +38,10 @@ impl State {
3338
}
3439
}
3540

36-
#[derive(ComponentType, Lower)]
37-
#[component(record)]
38-
struct ServiceRequest {
39-
headers: Vec<(Vec<u8>, Vec<u8>)>,
40-
body: Vec<u8>,
41-
}
42-
43-
impl ServiceRequest {
44-
async fn new(mut req: Request<State>) -> tide::Result<Self> {
45-
// Convert the tide request to a service request.
46-
let headers = req
47-
.iter()
48-
.map(|(n, v)| {
49-
(
50-
n.as_str().as_bytes().to_vec(),
51-
v.as_str().as_bytes().to_vec(),
52-
)
53-
})
54-
.collect();
55-
let body = req.take_body().into_bytes().await?;
56-
57-
Ok(Self { headers, body })
58-
}
59-
}
60-
61-
#[derive(ComponentType, Lift)]
62-
#[component(record)]
63-
struct ServiceResponse {
64-
headers: Vec<(Vec<u8>, Vec<u8>)>,
65-
body: Vec<u8>,
66-
}
67-
68-
impl TryFrom<ServiceResponse> for tide::Response {
41+
impl TryFrom<handler::Response> for tide::Response {
6942
type Error = tide::Error;
7043

71-
fn try_from(r: ServiceResponse) -> Result<Self, Self::Error> {
44+
fn try_from(r: handler::Response) -> Result<Self, Self::Error> {
7245
// Convert the service response to a tide response
7346
let mut builder = tide::Response::builder(StatusCode::Ok);
7447
for (name, value) in r.headers {
@@ -82,25 +55,16 @@ impl TryFrom<ServiceResponse> for tide::Response {
8255
}
8356
}
8457

85-
#[derive(ComponentType, Lift)]
86-
#[component(enum)]
87-
enum ServiceError {
88-
#[component(name = "bad-request")]
89-
BadRequest,
90-
}
91-
92-
impl From<ServiceError> for tide::Error {
93-
fn from(e: ServiceError) -> Self {
94-
match e {
95-
ServiceError::BadRequest => {
58+
impl handler::Error {
59+
fn into_tide(self) -> tide::Error {
60+
match self {
61+
Self::BadRequest => {
9662
tide::Error::from_str(StatusCode::BadRequest, "bad service request")
9763
}
9864
}
9965
}
10066
}
10167

102-
type ServiceResult = Result<ServiceResponse, ServiceError>;
103-
10468
/// WebAssembly component server.
10569
///
10670
/// A demonstration server that executes WebAssembly components.
@@ -144,24 +108,29 @@ impl ServerApp {
144108
app.listen(address).await.map_err(Into::into)
145109
}
146110

147-
async fn process_request(req: Request<State>) -> tide::Result {
148-
let state = req.state();
111+
async fn process_request(mut req: Request<State>) -> tide::Result {
112+
let body = req.body_bytes().await?;
113+
let headers = req
114+
.iter()
115+
.map(|(n, v)| (n.as_str().as_bytes(), v.as_str().as_bytes()))
116+
.collect::<Vec<_>>();
149117

150118
// Create a new store for the request
151-
let mut store = Store::new(&state.engine, ());
119+
let state = req.state();
152120
let linker: Linker<()> = Linker::new(&state.engine);
153-
154-
// Instantiate the service component and get its `execute` export
155-
let instance = linker.instantiate(&mut store, &state.component)?;
156-
let execute = instance
157-
.get_typed_func::<(ServiceRequest,), (ServiceResult,), _>(&mut store, "execute")?;
158-
159-
// Call the `execute` export with the request and translate the response
160-
execute
161-
.call(&mut store, (ServiceRequest::new(req).await?,))?
162-
.0
163-
.map_err(Into::into)
164-
.and_then(TryInto::try_into)
121+
let mut store = Store::new(&state.engine, ());
122+
let (service, _) = Service::instantiate(&mut store, &state.component, &linker)?;
123+
service
124+
.handler
125+
.call_execute(
126+
&mut store,
127+
handler::Request {
128+
headers: &headers,
129+
body: &body,
130+
},
131+
)?
132+
.map(TryInto::try_into)
133+
.map_err(handler::Error::into_tide)?
165134
}
166135
}
167136

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,26 @@
1-
record request {
2-
headers: list<tuple<list<u8>, list<u8>>>,
3-
body: list<u8>,
4-
}
1+
interface handler {
2+
record request {
3+
headers: list<tuple<list<u8>, list<u8>>>,
4+
body: list<u8>,
5+
}
6+
7+
record response {
8+
headers: list<tuple<list<u8>, list<u8>>>,
9+
body: list<u8>
10+
}
511

6-
record response {
7-
headers: list<tuple<list<u8>, list<u8>>>,
8-
body: list<u8>
12+
enum error {
13+
bad-request,
14+
}
15+
16+
execute: func(req: request) -> result<response, error>
917
}
1018

11-
enum error {
12-
bad-request,
19+
default world service {
20+
export handler: self.handler
1321
}
1422

15-
execute: func(req: request) -> result<response, error>
23+
world middleware {
24+
import downstream: self.handler
25+
export handler: self.handler
26+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# This file is automatically generated by cargo-component.
2+
# It is not intended for manual editing.
3+
version = 1

crates/wasm-compose/example/service/Cargo.toml

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,12 @@ edition = "2021"
55
publish = false
66

77
[dependencies]
8-
wit-bindgen-guest-rust = { git = "https://github.com/bytecodealliance/wit-bindgen", default_features = false }
8+
wit-bindgen = { version = "0.3.0", default_features = false }
99

1010
[lib]
1111
crate-type = ["cdylib"]
1212

1313
[package.metadata.component]
14-
direct-export = "service"
15-
16-
[package.metadata.component.exports]
17-
service = "../service.wit"
14+
target = { path = "../service.wit" }
1815

1916
[workspace]

0 commit comments

Comments
 (0)