cloudflare/workerdのTCP接続機能を試す
最近workerd(Cloudflare Workersのランタイム)で実装され始めたTCP socket接続の機能がある。
今まではHTTP越しにPostgreSQLサーバーなどに接続する必要があったが、これがあると接続先に直接繋ぐことができるというのがコンセプトのようだ(実装を読むと直接持続的な接続をするわけではなく間にアダプタを噛ませるアーキテクチャのようだけどまだ詳細は分からない)。
これをデバッグしようと思ったらそもそもbazel buildまでが結構難しかったので開発環境構築のメモもする。
システム環境
Name | Version |
---|---|
macOS | 12.6 |
Chip | Apple M1 Pro |
workerd | 1f8a561 |
bazel 5.3
bazel 5.3を要求してくるので固定する。
❯ gh repo clone cloudflare/workerd && cd workerd
❯ curl -fL https://releases.bazel.build/5.3.0/release/bazel-5.3.0-darwin-arm64 -o bazel && chmod +x bazel
❯ ./bazel --version
bazel 5.3.0
(追記)Bazeliskを利用するとさらに便利なようだ
python3パスの解決
❯ ./bazel build -c opt //src/workerd/server:workerd
unknown command: python3. Perhaps you have to reshim?
Target //src/workerd/server:workerd failed to build
コード生成にホスト側のpython3コマンドが呼び出されるのだけど見つからないことがある。自分の場合はasdf-pythonのバーチャルな環境を参照できていなかったので、システム直下のpython3を使うようにPATHで調整した。
./bazel build -c opt //src/workerd/server:workerd --action_env=PATH=/usr/bin:$PATH
# /usr/bin/python3が先頭に来ていればok
❯ PATH=/usr/bin:$PATH which -a python3
/usr/bin/python3
MacOSX12.3.sdkへの切り替え
external/v8/src/base/platform/platform-darwin.cc:56:22: error: 'getsectdatafromheader_64' is deprecated: first deprecated in macOS 13.0 [-Werror,-Wdeprecated-declarations]
char* code_ptr = getsectdatafromheader_64(
^~~~~~~~~~~~~~~~~~~~~~~~
use getsectiondata()
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.0.sdk/usr/include/mach-o/getsect.h:130:14: note: 'getsectdatafromheader_64' has been explicitly
marked deprecated here
extern char *getsectdatafromheader_64(
^
1 error generated.
Error in child process '/usr/bin/xcrun'. 1
workerdのソースコードは最新のXcodeとMacOSX13.0.sdkでコンパイルできないので回避する必要がある。方法は2つあって
- Envoyコミッタのdioさんがシェアしてるパッチを当てる
- ひとつ前のMacOSX12.3.sdkを用意して使う
今回は「ひとつ前のMacOSX12.3.sdkを用意して使う」にした。
Xcode旧バージョンを取ってくるには
- Apple Developer Account
- ダウンロードURL
が必要で、セットアップを簡単にするためにxcodes(複数Xcode管理ツール)を使う。
12系SDKが14.0.1に含まれるのがXcode Releases | xcodereleases.comを見て分かったので以下を実行する。
❯ xcodes install 14.0.1
Xcode 14.0.1 has been installed to /Applications/Xcode-14.0.1.app
❯ xcodes select 14.0.1
❯ xcrun --show-sdk-version
12.3
最終的にビルドコマンドの引数はこうなった
❯ ./bazel build -c opt //src/workerd/server:workerd --action_env=PATH=/usr/bin:$PATH --action_env=XCODE_VERSION_OVERRIDE=14.0.1
INFO: Elapsed time: 998.567s, Critical Path: 58.90s
INFO: 8345 processes: 3311 internal, 5033 darwin-sandbox, 1 local.
INFO: Build completed successfully, 8345 total actions
15分ぐらいかかってビルド完了。
connect() を試す
❯ ./bazel run //src/workerd/server:workerd --action_env=PATH=/usr/bin:$PATH -- serve $PWD/samples/tcp/config.capnp --watch --verbose
以下のようにfetcherからconnect()関数を呼び出してソケットを読み書きする。
GopherというのはGo言語のなにかというわけではなくて、HTTPより若いポート70のドキュメント転送プロトコルのこと。
netcatなどでgopherサーバーと直接対話できる。
❯ echo /fun | nc gopher.floodgap.com 70
1Floodgap Systems gopher root / gopher.floodgap.com 70
i error.host 1
i .o88o. o8o o8o error.host 1
i 888 `" `YP `YP error.host 1
i o888oo oooo oooo ooo. .oo. ' ooo. .oo. ' error.host 1
i 888 `888 `888 `888P"Y88b `888P"Y88b error.host 1
i 888 888 888 888 888 888 888 error.host 1
i 888 888 888 888 888 888 888 error.host 1
i o888o `V88V"V8P' o888o o888o o888o o888o error.host 1
i error.host 1
i .oooooooo .oooo. ooo. .oo. .oo. .ooooo. .oooo.o error.host 1
i888' `88b `P )88b `888P"Y88bP"Y88b d88' `88b d88( "8 error.host 1
i888 888 .oP"888 888 888 888 888ooo888 `"Y88b. error.host 1
i`88bod8P' d8( 888 888 888 888 888 .o o. )88b error.host 1
i`8oooooo. `Y888""8o o888o o888o o888o `Y8bod8P' 8""888P'
workerの動作については現状こんな感じ
❯ curl localhost:8080/
Socket connection failed: Error: internal error
workerd/jsg/util.c++:381: error: e = kj/compat/http.c++:5421: unimplemented: connect() is not implemented for NetworkHttpClient
kj::Promise<void> connect(
kj::StringPtr host, const kj::HttpHeaders& headers, ConnectResponse& tunnel) override {
// This code is hit when the global `connect` function is called in a JS worker script.
// It represents a proxy-less TCP connection, which means we can simply defer the handling of
// the connection to the service adapter (likely NetworkHttpClient). Its behaviour will be to
// connect directly to the host over TCP.
return serviceAdapter->connect(host, headers, tunnel);
}
アダプタを間に噛ますのかもと言っていた部分がこれで、NetworkHttpClientはworkerdをホストする環境(Cloudflare Workers)で実装され(たぶん)、ユーザー側としてはJSからconnect()を呼ぶソケット読み書きできる、というイメージになるのかと思った(なのでNode.jsのライブラリそのまま動かすとかはできなさそう)。
Discussion