TL;DR
wasi:http/proxyWorld を実装した WebAssembly Component で、ブラウザに Hello, world! を表示する。- 実装するのは
Guestトレイトのhandle関数だけだが、提供される操作がプリミティブなので、実際には Spin などのフレームワークを使う。
内容
フレームワークを使う理由
wasi:http/proxy を直接実装するのは学習用で、実際には Spin などのフレームワークを使う。理由は 2 つある。1 つは、IncomingRequest が提供する操作がプリミティブなものだけで、POST のデータやパス、クエリパラメータを取り出す処理を自作する必要があることである。もう 1 つは、URL のパスに応じて呼び出す Component を変えるルーティングの仕組みがないことである。
プロジェクトの作成
cargo-component で、wasi:http/proxy を対象にしたプロジェクトを作る。
cargo component new \
--lib \
--target wasi:http/proxy \
--namespace radish \
hello-wasi-http生成された src/lib.rs を見ると、Guest トレイトの handle 関数を実装すればよいことが分かる。
use bindings::exports::wasi::http::incoming_handler::{
Guest,
IncomingRequest,
ResponseOutparam,
};
struct Component;
impl Guest for Component {
fn handle(request: IncomingRequest, response_out: ResponseOutparam) {
unimplemented!()
}
}実装
handle 関数は、ホストコード(Component を利用するコード)から呼び出される。元になっているのは、wasi:http/proxy World が export している wasi:http/incoming-handler インタフェースの関数である。引数には、HTTP リクエストを表す IncomingRequest と、HTTP レスポンスを作るための ResponseOutparam が渡される。今回は Hello, world! を返すだけなので、ResponseOutparam だけを使う。
fn handle(_request: IncomingRequest, response_out: ResponseOutparam) {
// ヘッダの設定
let headers = [(
FieldKey::from("Content-Type"),
FieldValue::from("text/html"),
)];
let headers = Headers::from_list(&headers).unwrap();
// HTTP レスポンスの作成とステータスコードの設定
let response = OutgoingResponse::new(headers);
response.set_status_code(200).unwrap();
let body = response.body().unwrap();
// レスポンスボディの書き込み
let stream = body.write().unwrap();
stream.blocking_write_and_flush(b"Hello, world!").unwrap();
// レスポンスボディへの書き込みが終わったことを通知
drop(stream);
// レスポンスボディの操作が終わったことを通知
OutgoingBody::finish(body, None).unwrap();
// HTTP レスポンスの操作が成功したことを通知
ResponseOutparam::set(response_out, Ok(response));
}実行
wasi:http/proxy を実装した Component は、cargo component serve でビルドして実行できる。
$ cargo component serve
...
Serving HTTP on http://0.0.0.0:8080/localhost:8080 にアクセスすると、Hello, world! が表示される。