はじめに
WebAssembly Gateway Interface (WAGI) を試しに動かしてみました。
WAGIとは
WAGIは、Common Gateway Interface (CGI) のWebAssembly版の実装です。
ただし、いくつかCGIと異なる部分があります1。
2つほど抜粋すると、WAGIには次のような特徴があります。
- CGIではリクエストを受け取るたびに子プロセスを起動して処理をするが、WAGIではマルチスレッドで処理される
- ファイルシステムへのアクセスが制限される
- (これは、後述する
modules.toml
で指定することでそのファイルへのアクセスが許されるようになります。)
- (これは、後述する
インストール
今回は、Ubuntu20.04環境にビルド済みのバイナリをインストールします。
レポジトリのリリースぺ-ジからダウンロードして、/usr/local/bin
に配置します。
$ wget https://github.com/deislabs/wagi/releases/download/v0.4.0/wagi-v0.4.0-linux-amd64.tar.gz
$ tar -xvf wagi-v0.4.0-linux-amd64.tar.gz
$ sudo mv wagi /usr/local/bin
インストールができていることを確認します。
$ wagi --version
WAGI Server 0.4.0
とりあえず動かしてみる
WAGIのレポジトリにはexamples
フォルダがあり、WAGIを試しに実行できます。
$ git clone https://github.com/deislabs/wagi.git
$ cd wagi
$ wagi -c examples/modules.toml
デフォルトの場合、
localhost:3000
がリッスンされます。
外部からのリクエストを受け付けたい場合やポートを変更したい場合は-l
オプションを利用します。
例えば、次のようにして外部からのリクエストを3333ポートで受け付けるようにします。
bash
$ wagi -c examples/modules.toml -l 0.0.0.0:3333
別のターミナルからHTTPリクエストを送信して、レスポンスが返ってくることを確認します。
```shell-session:bash
$ curl -v localhost:3000
* Trying 127.0.0.1:3000...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 3000 (#0)
> GET / HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< content-type: text/html;charset=UTF-8
< content-length: 12
< date: Fri, 17 Dec 2021 05:43:49 GMT
<
Oh hi world
* Connection #0 to host localhost left intact
Oh hi world
とレスポンスが返って来ました!
modules.toml
WAGIを起動した際に -c examples/modules.toml
とオプションを指定しました。
このmodules.toml
がWAGIのコンフィグファイルです。これに従って、WAGIはURIと対応するWebAssemblyモジュールを決定します。オプションで指定をしなかった場合は、CWDのmodules.toml
が読み込まれます。
examples/modules.toml
では、からエンドポイントが/
の部分を抜粋すると、次のようになっています。
[[module]]
# Example executing a Web Assembly Text file
route = "/"
module = "examples/hello.wat"
ここでは、route
とmodule
が指定されています。
WAGIは/
にアクセスが来たら、examples/hello.wat
を用いてHTTPレスポンスを作って返します。
examples/hello.wat
を確認すると10行目にHTTPレスポンスがハードコードされていることが確認できます。
(module
;; This is the example Hello World WAT from the documentation at
;; https://github.com/bytecodealliance/wasmtime/blob/main/docs/WASI-tutorial.md
;;
;; It has been adapted to send CGI headers.
(import "wasi_snapshot_preview1" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32)))
(memory 1)
(export "memory" (memory 0))
(data (i32.const 8) "content-type: text/html;charset=UTF-8\n\nOh hi world\n")
(func $main (export "_start")
(i32.store (i32.const 0) (i32.const 8))
(i32.store (i32.const 4) (i32.const 51))
(call $fd_write
(i32.const 1)
(i32.const 0)
(i32.const 1)
(i32.const 20)
)
drop
)
)
エンドポイントを追加する
最後に、適当なWebAssemblyモジュールを作成して、WAGIのエンドポイントに追加します。
HTMLファイルを読んで、HTTPヘッダをつけてレスポンスを作成するようにします。
今回は以下のcat.rs
とcat.html
を用意しました。
fn main() {
println!("Content-Type: text/html; charset=UTF-8");
println!();
let html = std::fs::read_to_string("cat.html").unwrap_or(String::from("にゃー"));
println!("{}", html);
}
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>CAT</title>
</head>
<body>
<pre>
_,,...='~1
_,.、='"~~,才</彡 }
,.=゙ー- ▼ ````.X彡-ナ
_,,.::=7;;;.〃 人 .〟二二キ二
_,..='´~こ/;;;´,,.. -イ .`'''´ Y
.< 二二=オ;;,,..-´ }
乂 .二=壬 / /
ゝ、,,,,,..キ;/ ,..--./
ゞi ,/ \
\ _,,,,,__.レ i
\,./ ゝ_r_,.イ゙ !
/ ¦ \ ,. 、
{ ゝJ┘ .>-、, /6 6∂
\ ゙'i .ω }
゙キ i /
キ i /
キ;; /
メ゙゙ ,,ノ
{キ;. ノ
キ≠ r=ォ'匕ゝ、_ ,,....,
Y.--'''''''' ノ弋≠ ≠;≠キ千;;彡゙
i゙ o 3 ,.) `'''…ー'''''´´
(__ 8 ,..〟'''´´
ゝ-'''´
</pre>
</body>
</html>
cat.rs
をコンパイルして、cat.wasm
を作成します。
$ rustc --target=wasm32-wasi examples/cat.rs -o examples/cat.wasm
modules.toml
には、次のように追記します。
# 省略
[[module]]
route = "/cat"
module = "examples/cat.wasm"
volumes = {"." = "examples"}
WebAssemblyモジュールにファイルへのアクセスを許可するため、volumes
ディレクティブを利用します。
今回は、examples
フォルダをモジュールの.
にマウントしています。
(Toml Parserが Expected "}", [ \t] or [A-Za-z0-9_\-] but "\"" found.
と警告してくるように、本来のTomlの構文に違反しているのはちょっと気になりますね。)
これで準備が整ったので、WAGIを再起動します。
$ wagi -c examples/modules.toml
No log_dir specified, using temporary directory /tmp/.tmpsPMv9G for logs
別のターミナルから、/cat
にアクセスしてみます。
$ curl -v localhost:3000/cat
* Trying 127.0.0.1:3000...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 3000 (#0)
> GET /cat HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< content-type: text/html; charset=UTF-8
< content-length: 2140
< date: Fri, 17 Dec 2021 07:33:58 GMT
<
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>CAT</title>
</head>
<body>
<pre>
_,,...='~1
_,.、='"~~,才</彡 }
,.=゙ー- ▼ ````.X彡-ナ
_,,.::=7;;;.〃 人 .〟二二キ二
_,..='´~こ/;;;´,,.. -イ .`'''´ Y
.< 二二=オ;;,,..-´ }
乂 .二=壬 / /
ゝ、,,,,,..キ;/ ,..--./
ゞi ,/ \
\ _,,,,,__.レ i
\,./ ゝ_r_,.イ゙ !
/ ¦ \ ,. 、
{ ゝJ┘ .>-、, /6 6∂
\ ゙'i .ω }
゙キ i /
キ i /
キ;; /
メ゙゙ ,,ノ
{キ;. ノ
キ≠ r=ォ'匕ゝ、_ ,,....,
Y.--'''''''' ノ弋≠ ≠;≠キ千;;彡゙
i゙ o 3 ,.) `'''…ー'''''´´
(__ 8 ,..〟'''´´
ゝ-'''´
</pre>
</body>
</html>
* Connection #0 to host localhost left intact
かわいいですね🐈