地方エンジニアの学習日記

興味ある技術の雑なメモだったりを書いてくブログ。たまに日記とガジェット紹介。

【Go】GOOS=wasip1でビルドしたwasmでTCPサーバを起動する

github.com

wasip1ではネットワーク関連の機能が一部しかサポートされてないので使えないと思っていたがstealthrocket/netを使うことでできる模様。

こんな感じのDockerfileを用意しておく。このライブラリではWasmEdgeをサポートしているらしいのでそちらをインストール

FROM golang:1.21.4-bookworm
RUN curl -sSf https://raw.githubusercontent.com/WasmEdge/WasmEdge/master/utils/install.sh | bash
COPY main.wasm /main.wasm

あとはGoでhttpサーバを動かす際にライブラリを指定することで実行できるようになっている。ライブラリではWASI環境でソケット操作を行うための関数群を定義されていてそちらを通してWASMランタイムの機能を呼び出してソケット操作をすることで実現しているようです。

package main

import (
    "net/http"

    "github.com/stealthrocket/net/wasip1"
)

func main() {
    listener, err := wasip1.Listen("tcp", "127.0.0.1:3000")
    if err != nil {
        panic(err)
    }

    server := &http.Server{
        Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            w.Write([]byte("Hello, World!"))
            w.WriteHeader(http.StatusOK)
        }),
    }

    if err := server.Serve(listener); err != nil {
        panic(err)
    }
}

中身を見る

listenの先を見るとこんな実装がされています。sock_listen()を呼び出してerrnoをチェックしています。

func listen(fd int, backlog int) error {
    if errno := sock_listen(int32(fd), int32(backlog)); errno != 0 {
        return errno
    }
    return nil
}

sock_listen()は以下のような実装になっていました。go:wasmimport wasi_snapshot_preview1 sock_listenはWASMモジュール内でWASIのwasi_snapshot_preview1モジュールからsock_listenをインポートするように指示しています。

//go:wasmimport wasi_snapshot_preview1 sock_listen
//go:noescape
func sock_listen(fd int32, backlog int32) syscall.Errno