Skip to content

Commit 0fa1b2d

Browse files
authored
Merge pull request #28 from yoshuawuyts/use-wstd
Changes the Rust implementation to use `wstd`
2 parents 53279f5 + 017564b commit 0fa1b2d

File tree

4 files changed

+189
-49
lines changed

4 files changed

+189
-49
lines changed

Cargo.lock

+118-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ package = "component:rust-wasi-hello"
1414
proxy = true
1515

1616
[dependencies]
17-
wasi = "0.14.0"
1817
wit-bindgen-rt = { version = "0.37.0", features = ["bitflags"] }
18+
wstd = "0.5.0"

README.md

+12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@
55
An example project showing how to build an HTTP server for WASI 0.2 built in
66
Rust.
77

8+
## Routes
9+
10+
The following HTTP routes are available from the component:
11+
12+
```text
13+
/ # Hello world
14+
/wait # Sleep for one second
15+
/echo # Echo the HTTP body
16+
/echo-headers # Echo the HTTP headers
17+
/echo-trailers # Echo the HTTP trailers
18+
```
19+
820
## Installation
921

1022
The easiest way to try this project is by opening it in a GitHub Codespace. This

src/lib.rs

+58-46
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,72 @@
1-
pub use wasi::http::types::{
2-
Fields, IncomingRequest, OutgoingBody, OutgoingResponse, ResponseOutparam,
3-
};
4-
5-
struct Component;
6-
wasi::http::proxy::export!(Component);
7-
8-
impl wasi::exports::http::incoming_handler::Guest for Component {
9-
fn handle(req: IncomingRequest, outparam: ResponseOutparam) {
10-
match req.path_with_query().unwrap().as_str() {
11-
"/wait" => http_wait(req, outparam),
12-
// "/echo" => {} // TODO
13-
// "/host" => {} // TODO
14-
"/" | _ => http_home(req, outparam),
15-
}
1+
use wstd::http::body::{BodyForthcoming, IncomingBody};
2+
use wstd::http::server::{Finished, Responder};
3+
use wstd::http::{IntoBody, Request, Response, StatusCode};
4+
use wstd::io::{copy, empty, AsyncWrite};
5+
use wstd::time::{Duration, Instant};
6+
7+
#[wstd::http_server]
8+
async fn main(req: Request<IncomingBody>, res: Responder) -> Finished {
9+
match req.uri().path_and_query().unwrap().as_str() {
10+
"/wait" => wait(req, res).await,
11+
"/echo" => echo(req, res).await,
12+
"/echo-headers" => echo_headers(req, res).await,
13+
"/echo-trailers" => echo_trailers(req, res).await,
14+
"/" => home(req, res).await,
15+
_ => not_found(req, res).await,
1616
}
1717
}
1818

19-
fn http_home(_req: IncomingRequest, outparam: ResponseOutparam) {
20-
let headers = Fields::new();
21-
let res = OutgoingResponse::new(headers);
22-
let body = res.body().expect("outgoing response");
23-
24-
ResponseOutparam::set(outparam, Ok(res));
25-
26-
let out = body.write().expect("outgoing stream");
27-
out.blocking_write_and_flush(b"Hello, wasi:http/proxy world!\n")
28-
.expect("writing response");
29-
30-
drop(out);
31-
OutgoingBody::finish(body, None).unwrap();
19+
async fn home(_req: Request<IncomingBody>, res: Responder) -> Finished {
20+
// To send a single string as the response body, use `res::respond`.
21+
res.respond(Response::new("Hello, wasi:http/proxy world!\n".into_body()))
22+
.await
3223
}
3324

34-
fn http_wait(_req: IncomingRequest, outparam: ResponseOutparam) {
25+
async fn wait(_req: Request<IncomingBody>, res: Responder) -> Finished {
3526
// Get the time now
36-
let now = wasi::clocks::monotonic_clock::now();
27+
let now = Instant::now();
3728

38-
// Sleep for 1 second
39-
let nanos = 1_000_000_000;
40-
let pollable = wasi::clocks::monotonic_clock::subscribe_duration(nanos);
41-
pollable.block();
29+
// Sleep for one second.
30+
wstd::task::sleep(Duration::from_secs(1)).await;
4231

4332
// Compute how long we slept for.
44-
let elapsed = wasi::clocks::monotonic_clock::now() - now;
45-
let elapsed = elapsed / 1_000_000; // change to millis
33+
let elapsed = Instant::now().duration_since(now).as_millis();
4634

47-
let headers = Fields::new();
48-
let res = OutgoingResponse::new(headers);
49-
let body = res.body().expect("outgoing response");
35+
// To stream data to the response body, use `res::start_response`.
36+
let mut body = res.start_response(Response::new(BodyForthcoming));
37+
let result = body
38+
.write_all(format!("slept for {elapsed} millis\n").as_bytes())
39+
.await;
40+
Finished::finish(body, result, None)
41+
}
5042

51-
ResponseOutparam::set(outparam, Ok(res));
43+
async fn echo(mut req: Request<IncomingBody>, res: Responder) -> Finished {
44+
// Stream data from the req body to the response body.
45+
let mut body = res.start_response(Response::new(BodyForthcoming));
46+
let result = copy(req.body_mut(), &mut body).await;
47+
Finished::finish(body, result, None)
48+
}
49+
50+
async fn echo_headers(req: Request<IncomingBody>, responder: Responder) -> Finished {
51+
let mut res = Response::builder();
52+
*res.headers_mut().unwrap() = req.into_parts().0.headers;
53+
let res = res.body(empty()).unwrap();
54+
responder.respond(res).await
55+
}
5256

53-
let out = body.write().expect("outgoing stream");
54-
let msg = format!("slept for {elapsed} millis\n");
55-
out.blocking_write_and_flush(msg.as_bytes())
56-
.expect("writing response");
57+
async fn echo_trailers(req: Request<IncomingBody>, res: Responder) -> Finished {
58+
let body = res.start_response(Response::new(BodyForthcoming));
59+
let (trailers, result) = match req.into_body().finish().await {
60+
Ok(trailers) => (trailers, Ok(())),
61+
Err(err) => (Default::default(), Err(std::io::Error::other(err))),
62+
};
63+
Finished::finish(body, result, trailers)
64+
}
5765

58-
drop(out);
59-
OutgoingBody::finish(body, None).unwrap();
66+
async fn not_found(_req: Request<IncomingBody>, responder: Responder) -> Finished {
67+
let res = Response::builder()
68+
.status(StatusCode::NOT_FOUND)
69+
.body(empty())
70+
.unwrap();
71+
responder.respond(res).await
6072
}

0 commit comments

Comments
 (0)