- 限界開発鯖 Advent Calender 2022
6 日目32 日目 です (年末に投稿したかったのに… あ, 明けましておめでとうございます) - 今話題沸騰中の WASI を Rust からコンパイルして Wasmtime 上から HTTP で echo をするまでの記録です
- Rust のことについて, 特に断りは入れません
前置き
WASI, The WebAssembly System Interfaceという 変態的 革新的技術をご存知でしょうか. めちゃめちゃざっくり言うと, WASM の親戚みたいな感じで, 尚且つ syscall とかが絡むようなこともできちゃうっていうか syscall ができる (多分) すごーいやつです.
で, 今回お世話になる WASI の実行環境, ランタイムは Wasmtime というやつです. なんか Lucet ってやつが有ったり無かったりしたとか, あと WasmEdge とかいうくそ速そうなやつや, Wasmer っていうエコシステム呼ばわりされているものがあったりしますが, 今回は割愛, ソースは ここ. でもそもそも Wasmtime 以外今回の要件を満たしていないのかな?なんて気もしている. ちなみに Wasmtime の実装は Rust です, 嬉しい! (キャッキャ)
まあそんな革新的技術の 闇 実態に触れつつ, クロスプラットフォームな HTTP サーバーの WASI バイナリを吐かせよう!という試みについての 記録 です. 何か明確な方向性が有るわけでは御座いませんので悪しからず.
環境
> uname -snrmo
Linux archlinux 5.15.85-1-lts x86_64 GNU/Linux
> rustup -V
rustup 1.25.1 (2022-11-01)
info: This is the version for the rustup toolchain manager, not the rustc compiler.
info: The currently active `rustc` version is `rustc 1.68.0-nightly (d0dc9efff 2022-12-18)`
[target.'cfg(all())']
rustflags = ["-C", "target-cpu=native", "-Z", "share-generics=y"]
もしお手元で試す時に nightly
が必要そうだったら適位導入してください. 多分 stable
でも大丈夫です.
導入
まず主役となる Wasmtime を導入します. Archlinux を使っているので, これは Package Manager (pacman) で一発です (うれしい).
> sudo pacman -S wasmtime
resolving dependencies...
looking for conflicting packages...
Package (1) New Version Net Change
community/wasmtime 4.0.0-1 21.66 MiB
Total Installed Size: 21.66 MiB
...
ダウンロード時のことですが, 圧縮時のパッケージサイズ もとい *.pkg.tar.zst
のファイルサイズは 4.2MiB とのことで, けっこう小さいじゃーんという気持ち
次に, 今回の作業場所を作成します.
> cargo new http-wasi-rs
Created binary (application) `http-wasi-rs` package
> cd http-wasi-rs
あとは 思想を反映 下準備をして…
[package]
name = "http-wasi-rs"
version = "0.0.0"
edition = "2021"
fn main() {
println!("hi, from rust on wasmtime!");
}
そしたら, バイナリを作成!
...
'-prefetchwt1' is not a recognized feature for this target (ignoring feature)
'+cmov' is not a recognized feature for this target (ignoring feature)
'-avx512vbmi' is not a recognized feature for this target (ignoring feature)
'-shstk' is not a recognized feature for this target (ignoring feature)
'+movbe' is not a recognized feature for this target (ignoring feature)
'-avx512vp2intersect' is not a recognized feature for this target (ignoring feature)
'+xsaveopt' is not a recognized feature for this target (ignoring feature)
'-avx512dq' is not a recognized feature for this target (ignoring feature)
'+sse2' is not a recognized feature for this target (ignoring feature)
'+adx' is not a recognized feature for this target (ignoring feature)
'+sse3' is not a recognized feature for this target (ignoring feature)
'skylake' is not a recognized processor for this target (ignoring processor)
'skylake' is not a recognized processor for this target (ignoring processor)
'skylake' is not a recognized processor for this target (ignoring processor)
'skylake' is not a recognized processor for this target (ignoring processor)
'skylake' is not a recognized processor for this target (ignoring processor)
Finished dev [unoptimized + debuginfo] target(s) in 0.48s
え?うるさ. なんなんこれ. と思いきや, 前述の -C target-cpu=native
が影響している模様. .cargo/config.toml
で修正しようにもなんか上手く行かなかったので, 以下のように修正.
[target.'cfg(all())']
rustflags = ["-Z", "share-generics=y"]
【余談】rustflags
の適用順について
補足情報として,
> rustc --target wasm32-wasi --print target-cpus
Available CPUs for this target:
bleeding-edge
generic
mvp
> rustc --target x86_64-unknown-linux-gnu --print target-cpus
Available CPUs for this target:
native - Select the CPU of the current host (currently skylake).
alderlake
amdfam10
athlon
...
ですから wasm32-wasi
に target-cpu=native
なんて指定は意味が無いはずなのですが, x86_64 と同様に色々解釈されてしまっているようなのですよね.
先に提示したくっそうるさい出力の全容なのですが,
'<FEATURE/PROCESSOR>' is not a recognized {feature,processor} for this target (ignoring {feature,processor})
なんか出力がバグってそう (引用符の位置や個数がおかしかったり, 括弧書きだけ改行されたり複数回出力されたり…) なのでこの限りではありませんが, おおよそこんな感じ.
で, ~/.cargo/config.toml
で [target.*]
を全部コメントアウト, 消してみて .cargo/config.toml
をいじくりまわしてみた. (多分適応順には変わりがない)
[target.'cfg(all())']
rustflags = ["-C", "target-cpu=before"]
[target.wasm32-wasi]
rustflags = ["-C", "target-cpu=after"]
すると,
> cargo build -vv --target wasm32-wasi
Compiling http-wasi-rs v0.0.0 ($HOME/http-wasi-rs)
Running `... -C target-cpu=after -C target-cpu=before -Z share-generics=y`
'before' is not a recognized processor for this target (ignoring processor)
'before' is not a recognized processor for this target (ignoring processor)
'before' is not a recognized processor for this target (ignoring processor)
'before' is not a recognized processor for this target (ignoring processor)
'before' is not a recognized processor for this target (ignoring processor)
...
Finished dev [unoptimized + debuginfo] target(s) in 0.19s
でじゃあこれ toml 上での指定順に関わりあるんか?ってことで [target.wasm32-wasi]
を上にもってきたけど, 変わらず.
じゃあ公式の docs はどうなってんだっていうと, 該当するのがここ.
Configuration - The Cargo Book #build.rustflags
There are four mutually exclusive sources of extra flags. They are checked in order, with the first one being used:
CARGO_ENCODED_RUSTFLAGS
environment variable.RUSTFLAGS
environment variable.- All matching
target.<triple>.rustflags
andtarget.<cfg>.rustflags
config entries joined together.build.rustflags
config value.Additional flags may also be passed with the cargo rustc command.
多分だけど, 厳密には結合順が target.<triple>.rustflags
+ target.<cfg>.rustflags
ってことなんだろうな, と憶測. なのでもうどうしようもないので, -C target-cpu=native
はいつもお使いの x86_64-unknown-linux-gnu
へ指定しときました.
てなわけで, 余談でした.
> cargo build --target wasm32-wasi
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
ふう, すっきりだぜ. そしたら, さっさと実行してみる.
> wasmtime target/wasm32-wasi/debug/http-wasi-rs.wasm
hi, from rust on wasmtime!
phew, 良い眺めだ.
using TcpListener
of std
本題の HTTP 通信をする部分ですが, ちょっとこれが良くわからない.
詳しくは書きませんが, いつものように tokio
+ warp
で構築しようと思ったら, tokio
は WASM 向けでは 一部の features しか対応していない ことが原因で build が通らない. じゃあ tokio
+ hyper
は?というと, サーバーを立てるのに必須な hyper/tcp
の build が同様の理由で通らない.
というわけで, 取り敢えず接続の待ち受けまでは自分で書いてみることにした.
そして実装がこちら, 参考は これ.
use std::net::*;
use std::os::fd::*;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let listener = unsafe { TcpListener::from_raw_fd(4) };
listener.set_nonblocking(true)?;
for result in listener.incoming() {
match result {
Ok(_) => {},
Err(error) => {
eprintln!("error: {error}");
},
}
}
Ok(())
}
とりあえず実行しようってことで具体的な処理については書いていない. で実行.
> cargo build --target wasm32-wasi && wasmtime run --tcplisten 127.0.0.1:4000 target/wasm32-wasi/debug/http-wasi-rs.wasm
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Error: Os { code: 8, kind: Uncategorized, message: "Bad file descriptor" }
ああ, そうですか… ってちょいちょいちょい, 困ります.
幸いなことに commit へのリンクが 貼ってあった りしたが, 正直参考にならなかったのでさっさと Issue を探すことに. で見つけた (PR だけど).
どうやら --env 'LISTEN_FDS=1'
を指定してやれば良いのかな?
> wasmtime run --tcplisten 127.0.0.1:4000 --env 'LISTEN_FDS=1' target/wasm32-wasi/debug/http-wasi-rs.wasm
Error: Os { code: 8, kind: Uncategorized, message: "Bad file descriptor" }
だめらしい.
ので PR 主が提示している example を見てみることに.
let stdlistener = unsafe { std::net::TcpListener::from_raw_fd(3) };
えぇ (fd 違うじゃん)
> wasmtime run --tcplisten 127.0.0.1:4000 --env 'LISTEN_FDS=1' target/wasm32-wasi/debug/http-wasi-rs.wasm
...
error: Resource temporarily unavailable (os error 6)
error: Resource temporarily unavailable (os error 6)
error: Resource temporarily unavailable (os error 6)
error: Resource temporarily unavailable (os error 6)
error: Resource temporarily unavailable (os error 6)
error: Resource temporarily unavailable (os error 6)
error: Resource temporarily unavailable (os error 6)
error: Resource temporarily unavailable (os error 6)
error: Resource temporarily unavailable (os error 6)
error: Resource temporarily unavailable (os error 6)
error: Resource temporarily unavailable (os error 6)
...
えぇ… (処理を書き間違えた)
> wasmtime run --tcplisten 127.0.0.1:4000 target/wasm32-wasi/debug/http-wasi-rs.wasm
...
error: Resource temporarily unavailable (os error 6)
error: Resource temporarily unavailable (os error 6)
error: Resource temporarily unavailable (os error 6)
...
えぇ…… (しかも環境変数要らないじゃん)
なんか動きそうだし, さっさと本題の処理も書いてしまおう.
use std::net::*;
use std::os::fd::*;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let listener = unsafe { TcpListener::from_raw_fd(3) };
listener.set_nonblocking(true)?;
for mut stream in listener.incoming().flatten() {
use std::io::*;
let mut buffer = Vec::new();
let mut before = '\0';
let mut len = None;
for c in Stream(&mut stream).filter(|c| *c != '\r') {
print!("{c}");
if c == '\n' {
let str = core::str::from_utf8(buffer.as_slice())?;
if let Some(str) = str.strip_prefix("Content-Length: ") {
len = Some(str.parse::<usize>()?);
}
if before == '\n' {
break;
}
before = c;
buffer.clear();
} else {
before = c;
buffer.push(c as u8);
}
}
let Some(len) = len else {
stream.shutdown(Shutdown::Both)?;
continue;
};
let mut content = Vec::new();
content.resize(len, 0);
stream.read_exact(content.as_mut_slice())?;
let content = core::str::from_utf8(content.as_slice())?;
let send = String::new()
+ "HTTP/1.1 200 OK"
+ "\n"
+ "Content-Type: text/plain"
+ "\n"
+ &format!("Content-Length: {}", content.len())
+ "\n"
+ "\n"
+ content;
stream.write_all(send.as_bytes())?;
stream.shutdown(Shutdown::Both)?;
}
Ok(())
}
struct Stream<'a>(&'a mut TcpStream);
impl Iterator for Stream<'_> {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
use std::io::Read;
let mut bytes = [0; 1];
let result = self.0.read_exact(&mut bytes);
if let Ok(()) = result {
Some(bytes[0] as char)
} else {
None
}
}
}
唐 突 な く そ で か 実 装
はい. (だってこうするしかなかったんだもん!)
解説すると:
- ヘッダから 1 文字ずつ読んでいる
-
Read::read_to_end
なんてしようもんならEOF
が来ないが為にブロックしてしまう -
Read::set_read_timeout
は使えないので, 無いバイトを読んだ時点でブロックする -
Stream
という構造体, イテレータを実装し, バイト単位で読み出し易いように -
\r
は処理するの面倒なので無視している (\n
だけでいいじゃん) -
Content-Length
だけパースしている- 他のヘッダは不必要なので無視している
-
-
Content-Length
分だけ末尾を読み出す - 最小限のヘッダと共に返答
Rust の Iterator の恩恵はこういうところに出るのだ…素晴らしい… (惚れ惚れ)
実行してみる.
> cargo build --target wasm32-wasi && wasmtime run --tcplisten 127.0.0.1:4000 target/wasm32-wasi/debug/http-wasi-rs.wasm
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
POST / HTTP/1.1
Host: 127.0.0.1:4000
User-Agent: curl/7.87.0
Accept: */*
Content-Type: text/plain
Content-Length: 4
^C
> curl -X POST -H 'Content-Type: text/plain' -d 'echo' 127.0.0.1:4000
echo%
ちゃんと返ってきてる.
fd の謎については, 多分 std{in,out,err}
の次に開いてるから…だと思うけど, どうなんだろう. もしそうだったらかなり不安定な実装だと思うのだが…
あと, --dir
オプションが兼ね合いでこわれる とかなんとかってのもあるので, 多分この辺は不安定なんでしょう. 取り敢えずそういうことにしときます.
using TcpListener
of tokio
生の TCP 接続とか扱ってたら埒が明かないので, 取り敢えず tokio
が使えることを確かめたい. さっきの PR で提示されてた example は mio
の fork だったし, 多分動くってことなんだろう.
というわけで依存を追加. 不安定と云われている flag も追加. バグを恐れずに行け?
[target.wasm32-wasi]
rustflags = ["--cfg", "tokio_unstable"]
この指定の仕方, "解ってね" ってメッセージを感じるけど, 英弱なので分からなかった どこにも書いてなかったので, tokio-rs/tokio
で .toml 内の "unstable" というキーワードを探した所, netlify.toml
に記述があるのを発見しましたが, 正攻法じゃないかもしれない.
↑ これは少し前まで書き置いていた文章.
実際には ここ に堂々と書いてありました. はい.
> cargo add tokio -F tokio/full
Updating crates.io index
Adding tokio v1.23.0 to dependencies.
Features:
+ bytes
+ fs
+ full
+ io-std
+ io-util
+ libc
+ macros
+ memchr
+ net
+ num_cpus
+ parking_lot
+ process
+ rt
+ rt-multi-thread
+ signal
+ signal-hook-registry
+ socket2
+ sync
+ time
+ tokio-macros
- mio
- stats
- test-util
- tracing
- windows-sys
良い眺めだ.
use std::net::*;
use std::os::fd::*;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
早速非同期の悦びを…
error[E0599]: no function or associated item named `new_multi_thread` found for struct `Builder` in the current scope
--> src/main.rs:61:5
|
61 | Ok(())
| ^^
| |
| function or associated item not found in `Builder`
| help: there is an associated function with a similar name: `new_current_thread`
…????? ああ, new_multi_thread
にはどう足掻いても対応してないのね. じゃあ…
#[tokio::main(flavor = "current_thread")]
あ, 通った通った. これなら使えるのね.
じゃあお待ちかね, TCP 接続待ち受けの非同期化と洒落込みましょうか.
但し tokio::net::TcpListener
は FromRawFd
じゃないので, std::net::TcpListener
を構築した上で tokio::net::TcpListener::from_std
を介して構築します.
で, 最初は イキって 折角ならと core::async_iter::AsyncIterator
を Stream
に実装してやろうかと思ったのですが, 極めて残念なことにこいつがまだ 実用化されてない 様子だったので, Iterator の恩恵は受けられなくなりましたとさ (元からそんなに受けてないけど).
use std::os::fd::*;
use tokio::net::*;
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let listener = unsafe { std::net::TcpListener::from_raw_fd(3) };
listener.set_nonblocking(true)?;
let listener = TcpListener::from_std(listener)?;
while let Ok((mut stream, _)) = listener.accept().await {
use tokio::io::*;
let mut buffer = Vec::new();
let mut before = '\0';
let mut len = None;
while let Ok(c) = stream.read_u8().await.map(|b| b as char) {
if c == '\r' {
continue;
}
print!("{c}");
if c == '\n' {
let str = core::str::from_utf8(buffer.as_slice())?;
if let Some(str) = str.strip_prefix("Content-Length: ") {
len = Some(str.parse::<usize>()?);
}
if before == '\n' {
break;
}
before = c;
buffer.clear();
} else {
before = c;
buffer.push(c as u8);
}
}
let Some(len) = len else {
stream.shutdown().await?;
continue;
};
let mut content = Vec::new();
content.resize(len, 0);
stream.read_exact(content.as_mut_slice()).await?;
let content = core::str::from_utf8(content.as_slice())?;
let send = String::new()
+ "HTTP/1.1 200 OK"
+ "\n"
+ "Content-Type: text/plain"
+ "\n"
+ &format!("Content-Length: {}", content.len())
+ "\n"
+ "\n"
+ content;
stream.write_all(send.as_bytes()).await?;
stream.shutdown().await?;
}
Ok(())
}
一部の関数呼び出しが tokio
流に変更されているのと, 随所に .await
が付いている以外は何も処理を変えてません. あとは前述の Stream
構造体が消えたので for _ in ...
の代替として,AsyncReadExt::read_u8
を while let Ok(_) = ...
で受けて使うようにしています.
で動作確認.
> cargo build --target wasm32-wasi && wasmtime run --tcplisten 127.0.0.1:4000 target/wasm32-wasi/debug/http-wasi-rs.wasm
Finished dev [unoptimized + debuginfo] target(s) in 0.02s
POST / HTTP/1.1
Host: 127.0.0.1:4000
User-Agent: curl/7.87.0
Accept: */*
Content-Type: text/plain
Content-Length: 4
^C
> curl -X POST -H 'Content-Type: text/plain' -d 'echo' 127.0.0.1:4000
echo%
代わり映えしないけど, ちゃんと動いていることは確認.
using Connection
of hyper
やっとこさ本題. 非同期になろうとも TCP 接続を直に触ってたら埒が明かないので, さっさと既成ライブラリをぶち込む!使うのは先述した hyper
です. 前述の warp
を使おうと思いましたが, 多分 socket2
に依存してると WASM 向けに build できないっぽい. Issue も 立ってる. ので hyper/tcp
も使えない.
ただ, それは v0 までの話のようで, v1 (投稿現在は Release Candicate) では Server
構造体が削除されているっぽい関係からも socket2
が不必要になっています. なので, hyper
のお作法に則り, 先の実装を書き換えてみることにします. まず依存を追加.
> cargo add hyper -F hyper/server -F hyper/http1
Updating crates.io index
Adding hyper v0.14.23 to dependencies.
Features:
+ http1
+ server
- __internal_happy_eyeballs_tests
- client
- ffi
- full
- h2
- http2
- libc
- nightly
- runtime
- socket2
- stream
- tcp
> cargo add hyper@1.0.0-rc.2
Updating crates.io index
Adding hyper v1.0.0-rc.2 to dependencies.
Features:
+ http1
+ server
- client
- ffi
- full
- h2
- http-body-util
- http2
- libc
- nightly
- socket2
で処理を書き換え.
use std::convert::Infallible;
use std::os::fd::*;
use hyper::http::Response;
use hyper::server::conn::http1::Builder;
use hyper::service::service_fn;
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let listener = unsafe { std::net::TcpListener::from_raw_fd(3) };
listener.set_nonblocking(true)?;
let listener = tokio::net::TcpListener::from_std(listener)?;
let service = service_fn(|req| async {
let body = req.into_body();
let res = Response::new(body);
Ok::<_, Infallible>(res)
});
while let Ok((stream, _)) = listener.accept().await {
let task = Builder::new().serve_connection(stream, service);
tokio::spawn(task);
}
Ok(())
}
そしたら実行.
cargo build --target wasm32-wasi && wasmtime run --tcplisten localhost:4000 target/wasm32-wasi/debug/http-wasi-rs.wasm
Finished dev [unoptimized + debuginfo] target(s) in 0.03s
^C
> curl -X POST -H 'Content-Type: text/plain' -d 'echo' localhost:4000
echo%
うむ. 出力は無いが, きちんと動作している. hyper/http2
を有効にすれば HTTP/2 も使える (但し取り持ちについては実装しなければならない).
using ...
結論から言うと, 今回の記事はこれでおしまいです. 詳しくはこれから書きます.
あとこれが原因で'22 年の年末投稿に間に合いませんでした. 検証したり情報整理したりは年末ではつらすぎたね…
さて, TCP 接続を直接触ったり, 接続を処理させたりしたものの, 実用の範囲ではない. ルーティングやメソッドで処理を振り分けってのはやっぱ最低限欲しいですよね.
というわけでまず warp
を試してみました. が, これがなんと内部依存の socket2
に阻まれ build が通らない. default-features = false
でも勿論だめ.
hyper
の話でも出てきましたが, では実際にはどのような原因で build が落ちているのか?
error[E0583]: file not found for module `sys`
--> $HOME/.cargo/registry/src/github.com-1ecc6299db9ec823/socket2-0.4.7/src/lib.rs:124:1
|
124 | mod sys;
| ^^^^^^^^
|
= help: to create the module `sys`, create file "$HOME/.cargo/registry/src/github.com-1ecc6299db9ec823/socket2-0.4.7/src/sys.rs" or "$HOME/.cargo/registry/src/github.com-1ecc6299db9ec823/socket2-0.4.7/src/sys/mod.rs"
このエラーが根本なのですが, まあ別にこれだけというわけではなくて,
error: Socket2 doesn't support the compile target
--> $HOME/.cargo/registry/src/github.com-1ecc6299db9ec823/socket2-0.4.7/src/lib.rs:127:1
|
127 | compile_error!("Socket2 doesn't support the compile target");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
はい. socket2
は WASI に対応してないんだってさ. なんなら README にも書いてある.
from https://github.com/rust-lang/socket2#os-support
Tier 1
These OSs are tested with each commit in the CI and must always pass the tests.
All functions/types/etc., excluding ones behind theall
feature, must work on
these OSs.
- Linux
- macOS
- Windows
Tier 2
These OSs are currently build in the CI, but not tested. Not all
functions/types/etc. may work on these OSs, even ones not behind theall
feature flag.
- Android
- FreeBSD
- Fuchsia
- iOS
- illumos
- NetBSD
- Redox
- Solaris
WASI の姿がないですね. そういうことでしょう.
でそれなら Wasmtime から提供されている API である wasmtime
を使って環境依存なランタイムを構築してしまう, というのも運用上の選択肢ですが, それって要するに CLI ツールの wasmtime
ですよね, ということで今回は割愛.
今回求めているのは全機能を Wasmtime ランタイム上に封じ込め, 1 つのバイナリで環境上の差異や動的リンクの一切を無くす, という所にあるからです (へぇ).
所で, "じゃあ socket2
に依存しないという選択肢は?" と思うのですが, warp
では厳しそうだと感じました.
warp
ではそもそも hyper
の v0 にある Server
API にかなり依存しているので, 置換も面倒だし hyper
を v1 に対応させるのも面倒くさそうだなぁ, という印象です. この辺 とか顕著ですよね.
ただ, ある程度の基盤を実装すれば Filter
をハンドラとして扱えるはずなのでどうにか使えるはず. まあ Filter
単体で API が存在しないので, どうせ build は通らないんですけどね.
じゃあ warp
以外, 例えば actix-web
は?というと, 結論から言えば socket2
が含まれているため無理, という感じです.
具体的には, …なんか actix-rt
内で依存している tokio
の version が…古いのかなぁ (よくわかんない). 見た感じ, 一部の API が存在しない (例えば tokio::net::*
とか, tokio::task::spawn_blocking
とか). あとなんか修正を試みてみたんですけど, cfg(tokio_unstable)
は有効はなずなのに "対応してないよ!" って compile_error!
で言われるんですよね (確か). ちょっと…お手上げ.
まあ fork して依存を更新してみても, 結局は socket2
に阻まれることとなりましたとさ.
じゃあ次に rocket
は?というと, これはなんと v4 なら build が通る! 但し, 現行の最新は v5 で, これは Release Candicate なので安定版としての最新版ではありますが, それ以外は古いです.
何が古いって, こんなメッセージが表示されるくらいで…
warning: the following packages contain code that will be rejected by a future version of Rust: traitobject v0.1.0
note: to see what the problems were, use the option `--future-incompat-report`, or run `cargo report future-incompatibilities --id 9`
まあ, そんなことはいいんだ. build が通るなら動くのでは!?と期待したのも束の間, 実際には TcpListener
或いは fd から構築するような低レベルな API が存在しないため, 使えませんでした (あーあ).
ちなみに v5 は socket2
依存が含まれてました. はい.
あと iron
とかどうなんでしょう? 全然知らないけど名前だけ聞くなぁ… と思ったらbuildが通る! (まあ古いだけなんですが…) しかも FromRawFd
な std::net::TcpListener
から構築できる!
これはチャンス!と思って色々いじくり回してみたのですが, 残念なことになんか非対応の操作をどこかでしているっぽくて (追い切れなかった…) 起動しませんでした. …くそう.
あ, でも最新リリースのv0.6.1はただでさえ 3年前 ('19) な上, masterもCI落ちてるし最終commitは'21年です. まあ…息を引き取ってるかな…
ちなみに rocket
と同様の警告が出ます. traitobject
のやつね.
おわりに
とあるプロジェクトのバックエンドを書き始めようとした時に「WASI向けに作れば?」と悪魔の囁きをしてきた友人, 感謝しているが私はあなたを許さない.
そのうち socket2
はWASI向けの実装がなされるのでしょうか?そもそもWASIの仕様って不安定なんですかね. 知らんけど. そういうのってあんまし上流に乗らないイメージがあるので, コアな人たちが実装して使うって感じになるんでしょうか.
私も詳しいことは知りませんので, もし指摘などありましたらコメントまで (うれしい).
Happy New Year '23! 本年もよろしくおねがいします!