From 4623ba618f365859835d63c083197028cb061cc1 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 13 Feb 2024 14:49:34 -0800 Subject: [PATCH] Update to wasi-cli@0.2.0 (#43) --- .github/workflows/main.yml | 3 + .gitignore | 1 + Cargo.lock | 1074 +++++++++++++---------- Cargo.toml | 18 +- build-adapter.sh | 13 +- lib/virtual_adapter.debug.wasm | Bin 258561 -> 230108 bytes lib/virtual_adapter.wasm | Bin 226946 -> 194283 bytes lib/wasi_snapshot_preview1.reactor.wasm | Bin 97330 -> 96733 bytes src/lib.rs | 137 ++- src/virt_deny/clocks.rs | 16 +- src/virt_deny/exit.rs | 9 +- src/virt_deny/http.rs | 202 +++-- src/virt_deny/random.rs | 14 +- src/virt_deny/sockets.rs | 207 +++-- src/virt_env.rs | 18 +- src/{virt_io/mod.rs => virt_io.rs} | 40 +- src/virt_io/clocks.rs | 75 -- src/virt_io/filesystem.rs | 255 ------ src/virt_io/http.rs | 229 ----- src/virt_io/io.rs | 195 ---- src/virt_io/sockets.rs | 345 -------- src/virt_io/stdio.rs | 95 -- src/walrus_ops.rs | 61 ++ tests/cases/encapsulate.toml | 2 +- tests/cases/fs-nested-dir-read.toml | 4 +- tests/virt.rs | 27 +- virtual-adapter/src/io.rs | 633 +++++++++---- virtual-adapter/src/lib.rs | 28 +- wit/deps/cli/command.wit | 2 +- wit/deps/cli/imports.wit | 12 +- wit/deps/cli/stdio.wit | 6 +- wit/deps/cli/terminal.wit | 18 +- wit/deps/clocks/monotonic-clock.wit | 4 +- wit/deps/clocks/wall-clock.wit | 2 +- wit/deps/clocks/world.wit | 2 +- wit/deps/filesystem/preopens.wit | 2 +- wit/deps/filesystem/types.wit | 6 +- wit/deps/filesystem/world.wit | 2 +- wit/deps/http/handler.wit | 43 + wit/deps/http/incoming-handler.wit | 24 - wit/deps/http/outgoing-handler.wit | 20 - wit/deps/http/proxy.wit | 47 +- wit/deps/http/types.wit | 610 ++++++++++--- wit/deps/io/error.wit | 2 +- wit/deps/io/poll.wit | 4 +- wit/deps/io/streams.wit | 23 +- wit/deps/io/world.wit | 2 +- wit/deps/random/insecure-seed.wit | 2 +- wit/deps/random/insecure.wit | 2 +- wit/deps/random/random.wit | 2 +- wit/deps/random/world.wit | 2 +- wit/deps/sockets/instance-network.wit | 6 +- wit/deps/sockets/ip-name-lookup.wit | 98 +-- wit/deps/sockets/network.wit | 271 +++--- wit/deps/sockets/tcp-create-socket.wit | 45 +- wit/deps/sockets/tcp.wit | 577 ++++++------ wit/deps/sockets/udp-create-socket.wit | 45 +- wit/deps/sockets/udp.wit | 473 +++++----- wit/deps/sockets/world.wit | 2 +- wit/virt.wit | 276 +++--- 60 files changed, 3094 insertions(+), 3239 deletions(-) rename src/{virt_io/mod.rs => virt_io.rs} (93%) delete mode 100644 src/virt_io/clocks.rs delete mode 100644 src/virt_io/filesystem.rs delete mode 100644 src/virt_io/http.rs delete mode 100644 src/virt_io/io.rs delete mode 100644 src/virt_io/sockets.rs delete mode 100644 src/virt_io/stdio.rs create mode 100644 wit/deps/http/handler.wit delete mode 100644 wit/deps/http/incoming-handler.wit delete mode 100644 wit/deps/http/outgoing-handler.wit diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 423ff59..e6da62e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -37,6 +37,9 @@ jobs: - name: Install wasm32-wasi target run: rustup target add wasm32-wasi + - name: Install wasm-tools + run: cargo install wasm-tools + - name: Build adapter run: ./build-adapter.sh diff --git a/.gitignore b/.gitignore index d14bda9..277d36f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target /tests/generated +/lib/package.wasm \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 371dded..b4bcb1e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,7 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ - "gimli 0.28.0", + "gimli 0.28.1", ] [[package]] @@ -19,13 +19,14 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.8.3" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +checksum = "42cd52102d3df161c77a887b608d7a4897d7cc112886a9537b738a887a03aaff" dependencies = [ "cfg-if", "once_cell", "version_check", + "zerocopy", ] [[package]] @@ -34,11 +35,20 @@ version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b" +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" -version = "0.5.0" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" +checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" dependencies = [ "anstyle", "anstyle-parse", @@ -50,43 +60,43 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.3" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "anstyle-parse" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "2.1.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" [[package]] name = "arbitrary" @@ -96,13 +106,13 @@ checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" [[package]] name = "async-trait" -version = "0.1.73" +version = "0.1.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.48", ] [[package]] @@ -128,9 +138,9 @@ dependencies = [ [[package]] name = "base64" -version = "0.21.4" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "bincode" @@ -179,9 +189,9 @@ checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" @@ -191,25 +201,25 @@ checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cap-fs-ext" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b779b2d0a001c125b4584ad586268fb4b92d957bff8d26d7fe0dd78283faa814" +checksum = "88e341d15ac1029aadce600be764a1a1edafe40e03cde23285bc1d261b3a4866" dependencies = [ - "cap-primitives 2.0.0", - "cap-std 2.0.0", - "io-lifetimes 2.0.2", - "windows-sys 0.48.0", + "cap-primitives 2.0.1", + "cap-std 2.0.1", + "io-lifetimes 2.0.3", + "windows-sys 0.52.0", ] [[package]] name = "cap-net-ext" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ffc30dee200c20b4dcb80572226f42658e1d9c4b668656d7cc59c33d50e396e" +checksum = "434168fe6533055f0f4204039abe3ff6d7db338ef46872a5fa39e9d5ad5ab7a9" dependencies = [ - "cap-primitives 2.0.0", - "cap-std 2.0.0", - "rustix 0.38.30", + "cap-primitives 2.0.1", + "cap-std 2.0.1", + "rustix 0.38.31", "smallvec", ] @@ -225,33 +235,33 @@ dependencies = [ "io-lifetimes 1.0.11", "ipnet", "maybe-owned", - "rustix 0.37.23", + "rustix 0.37.27", "windows-sys 0.48.0", "winx 0.35.1", ] [[package]] name = "cap-primitives" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf30c373a3bee22c292b1b6a7a26736a38376840f1af3d2d806455edf8c3899" +checksum = "fe16767ed8eee6d3f1f00d6a7576b81c226ab917eb54b96e5f77a5216ef67abb" dependencies = [ "ambient-authority", - "fs-set-times 0.20.0", - "io-extras 0.18.0", - "io-lifetimes 2.0.2", + "fs-set-times 0.20.1", + "io-extras 0.18.1", + "io-lifetimes 2.0.3", "ipnet", "maybe-owned", - "rustix 0.38.30", - "windows-sys 0.48.0", - "winx 0.36.2", + "rustix 0.38.31", + "windows-sys 0.52.0", + "winx 0.36.3", ] [[package]] name = "cap-rand" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "577de6cff7c2a47d6b13efe5dd28bf116bd7f8f7db164ea95b7cc2640711f522" +checksum = "20e5695565f0cd7106bc3c7170323597540e772bb73e0be2cd2c662a0f8fa4ca" dependencies = [ "ambient-authority", "rand", @@ -266,31 +276,33 @@ dependencies = [ "cap-primitives 1.0.15", "io-extras 0.17.4", "io-lifetimes 1.0.11", - "rustix 0.37.23", + "rustix 0.37.27", ] [[package]] name = "cap-std" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84bade423fa6403efeebeafe568fdb230e8c590a275fba2ba978dd112efcf6e9" +checksum = "593db20e4c51f62d3284bae7ee718849c3214f93a3b94ea1899ad85ba119d330" dependencies = [ - "cap-primitives 2.0.0", - "io-extras 0.18.0", - "io-lifetimes 2.0.2", - "rustix 0.38.30", + "cap-primitives 2.0.1", + "io-extras 0.18.1", + "io-lifetimes 2.0.3", + "rustix 0.38.31", ] [[package]] name = "cap-time-ext" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8f52b3c8f4abfe3252fd0a071f3004aaa3b18936ec97bdbd8763ce03aff6247" +checksum = "03261630f291f425430a36f38c847828265bc928f517cdd2004c56f4b02f002b" dependencies = [ - "cap-primitives 2.0.0", + "ambient-authority", + "cap-primitives 2.0.1", + "iana-time-zone", "once_cell", - "rustix 0.38.30", - "winx 0.36.2", + "rustix 0.38.31", + "winx 0.36.3", ] [[package]] @@ -311,9 +323,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.4.4" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d7b8d5ec32af0fadc644bf1fd509a688c2103b185644bb1e29d164e0703136" +checksum = "80c21025abd42669a92efc996ef13cfb2c5c627858421ea58d5c3b331a6c134f" dependencies = [ "clap_builder", "clap_derive", @@ -321,9 +333,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.4" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5179bb514e4d7c2051749d8fcefa2ed6d06a9f4e6d69faf3805f5d80b8cf8d56" +checksum = "458bf1f341769dfcf849846f65dffdf9146daa56bcd2a47cb4e1de9915567c99" dependencies = [ "anstream", "anstyle", @@ -333,21 +345,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.4.2" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" +checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.48", ] [[package]] name = "clap_lex" -version = "0.5.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "codespan-reporting" @@ -365,6 +377,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + [[package]] name = "cpp_demangle" version = "0.3.5" @@ -376,27 +394,27 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] name = "cranelift-bforest" -version = "0.103.0" +version = "0.104.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c22542c0b95bd3302f7ed6839869c561f2324bac2fd5e7e99f5cfa65fdc8b92" +checksum = "7e7c0d51205b863591dd1e7aaa0fb69c2ea7bed48ffa63d6c4a848b07a35a732" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.103.0" +version = "0.104.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b3db903ef2e9c8a4de2ea6db5db052c7857282952f9df604aa55d169e6000d8" +checksum = "9ffb467cbc25543e4c20d2ad669bf8275598047b03c89652ad5fe2a0f47fc0e1" dependencies = [ "bumpalo", "cranelift-bforest", @@ -405,8 +423,8 @@ dependencies = [ "cranelift-control", "cranelift-entity", "cranelift-isle", - "gimli 0.28.0", - "hashbrown 0.14.0", + "gimli 0.28.1", + "hashbrown 0.14.3", "log", "regalloc2", "smallvec", @@ -415,33 +433,33 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.103.0" +version = "0.104.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6590feb5a1d6438f974bf6a5ac4dddf69fca14e1f07f3265d880f69e61a94463" +checksum = "bc7e74aed5c2b91e38d090653506afbd2cd3be1ff70593e2aa6bb82b3c6b77ff" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.103.0" +version = "0.104.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7239038c56fafe77fddc8788fc8533dd6c474dc5bdc5637216404f41ba807330" +checksum = "9ff2dd24cce0775566da85770cb48aa58fef901cf2bff30275b42e7dbe62cbd5" [[package]] name = "cranelift-control" -version = "0.103.0" +version = "0.104.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7dc9c595341404d381d27a3d950160856b35b402275f0c3990cd1ad683c8053" +checksum = "e8bcf4d5c73bbca309edf3af2839b5218e5c74cfbf22b0ac492af8a1d11120d9" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.103.0" +version = "0.104.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44e3ee532fc4776c69bcedf7e62f9632cbb3f35776fa9a525cdade3195baa3f7" +checksum = "286754159b1a685475d6a0b4710832f950d6f4846a817002e2c23ff001321a65" dependencies = [ "serde", "serde_derive", @@ -449,9 +467,9 @@ dependencies = [ [[package]] name = "cranelift-frontend" -version = "0.103.0" +version = "0.104.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a612c94d09e653662ec37681dc2d6fd2b9856e6df7147be0afc9aabb0abf19df" +checksum = "67150a1fef9857caba710f8c0c8223d640f02c0e5d1ebbfc75ed62912599cb6b" dependencies = [ "cranelift-codegen", "log", @@ -461,15 +479,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.103.0" +version = "0.104.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85db9830abeb1170b7d29b536ffd55af1d4d26ac8a77570b5d1aca003bf225cc" +checksum = "eb7ceea70d3e0d7f69df7657f99de902e32016731c5a8d2788c1df0215f00952" [[package]] name = "cranelift-native" -version = "0.103.0" +version = "0.104.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301ef0edafeaeda5771a5d2db64ac53e1818ae3111220a185677025fe91db4a1" +checksum = "707e5d9384ce4fa3c40af1abf4c3ec49857745cced5187593385f4a2c0b95445" dependencies = [ "cranelift-codegen", "libc", @@ -478,9 +496,9 @@ dependencies = [ [[package]] name = "cranelift-wasm" -version = "0.103.0" +version = "0.104.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380f0abe8264e4570ac615fc31cef32a3b90a77f7eb97b08331f9dd357b1f500" +checksum = "d4d957e3ff2a14c2f974a66c22bfcedcd2bd0272af8dce4236869c3942f5a471" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -488,51 +506,43 @@ dependencies = [ "itertools", "log", "smallvec", - "wasmparser 0.118.1", + "wasmparser 0.118.2", "wasmtime-types", ] [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] [[package]] name = "crossbeam-deque" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ - "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.15" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "autocfg", - "cfg-if", "crossbeam-utils", - "memoffset", - "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.16" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crypto-common" @@ -546,9 +556,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.107" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe98ba1789d56fb3db3bee5e032774d4f421b685de7ba703643584ba24effbe" +checksum = "8aff472b83efd22bfc0176aa8ba34617dd5c17364670eb201a5f06d339b8abf7" dependencies = [ "cc", "cxxbridge-flags", @@ -558,9 +568,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.107" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4ce20f6b8433da4841b1dadfb9468709868022d829d5ca1f2ffbda928455ea3" +checksum = "bcf6e7a52c19013a9a0ec421c7d9c2d1125faf333551227e0a017288d71b47c3" dependencies = [ "cc", "codespan-reporting", @@ -568,24 +578,24 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.37", + "syn 2.0.48", ] [[package]] name = "cxxbridge-flags" -version = "1.0.107" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20888d9e1d2298e2ff473cee30efe7d5036e437857ab68bbfea84c74dba91da2" +checksum = "589e83d02fc1d4fb78f5ad56ca08835341e23499d086d2821315869426d618dc" [[package]] name = "cxxbridge-macro" -version = "1.0.107" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fa16a70dd58129e4dfffdff535fb1bce66673f7bbeec4a5a1765a504e1ccd84" +checksum = "e2cb1fd8ffae4230c7cfbbaf3698dbeaf750fa8c5dadf7ed897df581b9b572a5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.48", ] [[package]] @@ -659,9 +669,9 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "encoding_rs" @@ -702,19 +712,19 @@ checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fastrand" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "fd-lock" -version = "4.0.0" +version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b0377f1edc77dbd1118507bc7a66e4ab64d2b90c66f90726dc801e73a8c68f9" +checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" dependencies = [ "cfg-if", - "rustix 0.38.30", - "windows-sys 0.48.0", + "rustix 0.38.31", + "windows-sys 0.52.0", ] [[package]] @@ -733,9 +743,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -747,26 +757,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d167b646a876ba8fda6b50ac645cfd96242553cbaf0ca4fccaa39afcbf0801f" dependencies = [ "io-lifetimes 1.0.11", - "rustix 0.38.30", + "rustix 0.38.31", "windows-sys 0.48.0", ] [[package]] name = "fs-set-times" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd738b84894214045e8414eaded76359b4a5773f0a0a56b16575110739cdcf39" +checksum = "033b337d725b97690d86893f9de22b67b80dcc4e9ad815f348254c38119db8fb" dependencies = [ - "io-lifetimes 2.0.2", - "rustix 0.38.30", - "windows-sys 0.48.0", + "io-lifetimes 2.0.3", + "rustix 0.38.31", + "windows-sys 0.52.0", ] [[package]] name = "futures" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -778,9 +788,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -788,33 +798,33 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-core", "futures-sink", @@ -865,9 +875,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "libc", @@ -887,12 +897,12 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" dependencies = [ "fallible-iterator 0.3.0", - "indexmap 2.0.0", + "indexmap 2.2.3", "stable_deref_trait", ] @@ -913,9 +923,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ "ahash", ] @@ -940,9 +950,32 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" + +[[package]] +name = "iana-time-zone" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] [[package]] name = "id-arena" @@ -952,9 +985,9 @@ checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" [[package]] name = "idna" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -986,12 +1019,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.0" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown 0.14.3", "serde", ] @@ -1007,12 +1040,12 @@ dependencies = [ [[package]] name = "io-extras" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d3c230ee517ee76b1cc593b52939ff68deda3fae9e41eca426c6b4993df51c4" +checksum = "c301e73fb90e8a29e600a9f402d095765f74310d582916a952f618836a1bd1ed" dependencies = [ - "io-lifetimes 2.0.2", - "windows-sys 0.48.0", + "io-lifetimes 2.0.3", + "windows-sys 0.52.0", ] [[package]] @@ -1028,15 +1061,15 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "2.0.2" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bffb4def18c48926ccac55c1223e02865ce1a821751a95920448662696e7472c" +checksum = "5a611371471e98973dbcab4e0ec66c31a10bc356eeb4d54a0e05eac8158fe38c" [[package]] name = "ipnet" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "itertools" @@ -1049,9 +1082,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "ittapi" @@ -1075,13 +1108,22 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.26" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" dependencies = [ "libc", ] +[[package]] +name = "js-sys" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "leb128" version = "0.2.5" @@ -1090,9 +1132,20 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.152" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.2", + "libc", + "redox_syscall", +] [[package]] name = "link-cplusplus" @@ -1138,9 +1191,9 @@ checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" [[package]] name = "memchr" -version = "2.6.3" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "memfd" @@ -1148,7 +1201,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" dependencies = [ - "rustix 0.38.30", + "rustix 0.38.31", ] [[package]] @@ -1162,18 +1215,18 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "wasi", @@ -1192,21 +1245,21 @@ dependencies = [ [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "crc32fast", - "hashbrown 0.14.0", - "indexmap 2.0.0", + "hashbrown 0.14.3", + "indexmap 2.2.3", "memchr", ] [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "paste" @@ -1216,9 +1269,9 @@ checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "petgraph" @@ -1227,7 +1280,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 2.0.0", + "indexmap 2.2.3", ] [[package]] @@ -1244,9 +1297,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" [[package]] name = "ppv-lite86" @@ -1256,9 +1309,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.67" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -1274,9 +1327,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -1322,9 +1375,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" dependencies = [ "either", "rayon-core", @@ -1332,9 +1385,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -1342,30 +1395,21 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "redox_users" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ "getrandom", - "redox_syscall 0.2.16", + "libredox", "thiserror", ] @@ -1396,9 +1440,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.37.23" +version = "0.37.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" +checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" dependencies = [ "bitflags 1.3.2", "errno", @@ -1412,9 +1456,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.30" +version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ "bitflags 2.4.2", "errno", @@ -1433,15 +1477,9 @@ checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" - -[[package]] -name = "scopeguard" -version = "1.2.0" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "scratch" @@ -1451,35 +1489,35 @@ checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152" [[package]] name = "semver" -version = "1.0.18" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.48", ] [[package]] name = "serde_json" -version = "1.0.107" +version = "1.0.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" dependencies = [ "itoa", "ryu", @@ -1488,20 +1526,20 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" dependencies = [ "serde", ] [[package]] name = "serde_yaml" -version = "0.9.25" +version = "0.9.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574" +checksum = "adf8a49373e98a4c5f0ceb5d05aa7c648d75f63774981ed95b7c7443bbd50c6e" dependencies = [ - "indexmap 2.0.0", + "indexmap 2.2.3", "itoa", "ryu", "serde", @@ -1510,9 +1548,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.7" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", @@ -1546,15 +1584,15 @@ checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" [[package]] name = "smallvec" -version = "1.11.1" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "socket2" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", "windows-sys 0.48.0", @@ -1562,9 +1600,9 @@ dependencies = [ [[package]] name = "spdx" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b19b32ed6d899ab23174302ff105c1577e45a06b08d4fe0a9dd13ce804bbbf71" +checksum = "62bde1398b09b9f93fc2fc9b9da86e362693e999d3a54a8ac47a99a5a73f638b" dependencies = [ "smallvec", ] @@ -1591,9 +1629,9 @@ dependencies = [ [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" [[package]] name = "strum" @@ -1627,9 +1665,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.37" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", @@ -1638,18 +1676,18 @@ dependencies = [ [[package]] name = "system-interface" -version = "0.26.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27ce32341b2c0b70c144bbf35627fdc1ef18c76ced5e5e7b3ee8b5ba6b2ab6a0" +checksum = "0682e006dd35771e392a6623ac180999a9a854b1d4a6c12fb2e804941c2b1f58" dependencies = [ "bitflags 2.4.2", "cap-fs-ext", - "cap-std 2.0.0", + "cap-std 2.0.1", "fd-lock", - "io-lifetimes 2.0.2", - "rustix 0.38.30", - "windows-sys 0.48.0", - "winx 0.36.2", + "io-lifetimes 2.0.3", + "rustix 0.38.31", + "windows-sys 0.52.0", + "winx 0.36.3", ] [[package]] @@ -1660,44 +1698,43 @@ checksum = "69758bda2e78f098e4ccb393021a0963bb3442eac05f135c30f61b7370bbafae" [[package]] name = "tempfile" -version = "3.8.0" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" dependencies = [ "cfg-if", "fastrand", - "redox_syscall 0.3.5", - "rustix 0.38.30", - "windows-sys 0.48.0", + "rustix 0.38.31", + "windows-sys 0.52.0", ] [[package]] name = "termcolor" -version = "1.3.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.48" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.48" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.48", ] [[package]] @@ -1717,9 +1754,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.32.0" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", @@ -1734,13 +1771,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.48", ] [[package]] @@ -1754,9 +1791,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" dependencies = [ "serde", "serde_spanned", @@ -1766,20 +1803,20 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.19.15" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +checksum = "99e68c159e8f5ba8a28c4eb7b0c0c190d77bb479047ca713270048145a9ad28a" dependencies = [ - "indexmap 2.0.0", + "indexmap 2.2.3", "serde", "serde_spanned", "toml_datetime", @@ -1788,11 +1825,10 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "log", "pin-project-lite", "tracing-attributes", @@ -1801,20 +1837,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.48", ] [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", ] @@ -1827,9 +1863,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" @@ -1848,9 +1884,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" @@ -1866,15 +1902,15 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "unsafe-libyaml" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" +checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" [[package]] name = "url" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", @@ -1889,9 +1925,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.4.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" [[package]] name = "version_check" @@ -1943,45 +1979,45 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi-cap-std-sync" -version = "16.0.0" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "154528979a211aa28d969846e883df75705809ed9bcc70aba61460683ea7355b" +checksum = "025e842ba390587e523785ff58bd54fbbf1781b8d3072bc9aba4dc0b809f69da" dependencies = [ "anyhow", "async-trait", "cap-fs-ext", "cap-rand", - "cap-std 2.0.0", + "cap-std 2.0.1", "cap-time-ext", - "fs-set-times 0.20.0", - "io-extras 0.18.0", - "io-lifetimes 2.0.2", + "fs-set-times 0.20.1", + "io-extras 0.18.1", + "io-lifetimes 2.0.3", "once_cell", - "rustix 0.38.30", + "rustix 0.38.31", "system-interface", "tracing", "wasi-common", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "wasi-common" -version = "16.0.0" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d888b611fee7d273dd057dc009d2dd3132736f36710ffd65657ac83628d1e3b" +checksum = "da4d4023cc65b3615590d38db0afb79234de09b3bb89cb0d8f83bdee9f5c28a8" dependencies = [ "anyhow", "bitflags 2.4.2", "cap-rand", - "cap-std 2.0.0", - "io-extras 0.18.0", + "cap-std 2.0.1", + "io-extras 0.18.1", "log", - "rustix 0.38.30", + "rustix 0.38.31", "thiserror", "tracing", "wasmtime", "wiggle", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1994,35 +2030,89 @@ dependencies = [ "heck 0.4.1", "serde", "tokio", - "toml 0.7.8", + "toml 0.8.10", "walrus", "wasm-compose", "wasm-metadata", "wasm-opt", - "wasmparser 0.119.0", + "wasmparser 0.121.2", "wasmtime", "wasmtime-wasi", - "wit-component 0.19.1", + "wit-component", ] +[[package]] +name = "wasm-bindgen" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.48", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" + [[package]] name = "wasm-compose" -version = "0.5.0" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9941776e288d53b544a50d295554497c499516095634d92d63f0aa099685372a" +checksum = "fd324927af875ebedb1b820c00e3c585992d33c2c787c5021fe6d8982527359b" dependencies = [ "anyhow", "heck 0.4.1", "im-rc", - "indexmap 2.0.0", + "indexmap 2.2.3", "log", "petgraph", "serde", "serde_derive", "serde_yaml", "smallvec", - "wasm-encoder 0.39.0", - "wasmparser 0.119.0", + "wasm-encoder 0.41.2", + "wasmparser 0.121.2", "wat", ] @@ -2046,35 +2136,35 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.39.0" +version = "0.41.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "111495d6204760238512f57a9af162f45086504da332af210f2f75dd80b34f1d" +checksum = "972f97a5d8318f908dded23594188a90bcd09365986b1163e66d70170e5287ae" dependencies = [ "leb128", - "wasmparser 0.119.0", + "wasmparser 0.121.2", ] [[package]] name = "wasm-metadata" -version = "0.10.15" +version = "0.10.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "818931c85b1d197909699d36c509fa89550ccfa0d66932ba3c1726faddb4d0c7" +checksum = "18ebaa7bd0f9e7a5e5dd29b9a998acf21c4abed74265524dd7e85934597bfb10" dependencies = [ "anyhow", - "indexmap 2.0.0", + "indexmap 2.2.3", "serde", "serde_derive", "serde_json", "spdx", - "wasm-encoder 0.39.0", - "wasmparser 0.119.0", + "wasm-encoder 0.41.2", + "wasmparser 0.121.2", ] [[package]] name = "wasm-opt" -version = "0.114.1" +version = "0.114.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d005a95f934878a1fb446a816d51c3601a0120ff929005ba3bab3c749cfd1c7" +checksum = "effbef3bd1dde18acb401f73e740a6f3d4a1bc651e9773bddc512fe4d8d68f67" dependencies = [ "anyhow", "libc", @@ -2088,9 +2178,9 @@ dependencies = [ [[package]] name = "wasm-opt-cxx-sys" -version = "0.114.1" +version = "0.114.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d04e240598162810fad3b2e96fa0dec6dba1eb65a03f3bd99a9248ab8b56caa" +checksum = "c09e24eb283919ace2ed5733bda4842a59ce4c8de110ef5c6d98859513d17047" dependencies = [ "anyhow", "cxx", @@ -2100,9 +2190,9 @@ dependencies = [ [[package]] name = "wasm-opt-sys" -version = "0.114.1" +version = "0.114.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2efd2aaca519d64098c4faefc8b7433a97ed511caf4c9e516384eb6aef1ff4f9" +checksum = "36f2f817bed2e8d65eb779fa37317e74de15585751f903c9118342d1970703a4" dependencies = [ "anyhow", "cc", @@ -2118,40 +2208,40 @@ checksum = "449167e2832691a1bff24cde28d2804e90e09586a448c8e76984792c44334a6b" [[package]] name = "wasmparser" -version = "0.118.1" +version = "0.118.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ee9723b928e735d53000dec9eae7b07a60e490c85ab54abb66659fc61bfcd9" +checksum = "77f1154f1ab868e2a01d9834a805faca7bf8b50d041b4ca714d005d0dab1c50c" dependencies = [ - "indexmap 2.0.0", + "indexmap 2.2.3", "semver", ] [[package]] name = "wasmparser" -version = "0.119.0" +version = "0.121.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c35daf77afb4f9b14016625144a391085ec2ca99ca9cc53ed291bb53ab5278d" +checksum = "9dbe55c8f9d0dbd25d9447a5a889ff90c0cc3feaa7395310d3d826b2c703eaab" dependencies = [ "bitflags 2.4.2", - "indexmap 2.0.0", + "indexmap 2.2.3", "semver", ] [[package]] name = "wasmprinter" -version = "0.2.76" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac2a7745372074e5573e365e17100f5a26058740576313784ef03fb900ea8d2" +checksum = "60e73986a6b7fdfedb7c5bf9e7eb71135486507c8fbc4c0c42cffcb6532988b7" dependencies = [ "anyhow", - "wasmparser 0.119.0", + "wasmparser 0.121.2", ] [[package]] name = "wasmtime" -version = "16.0.0" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8e539fded2495422ea3c4dfa7beeddba45904eece182cf315294009e1a323bf" +checksum = "8acb6aa966be38f613954c3debe7ba6c7a02ffd0537432be438da0b038955cdf" dependencies = [ "anyhow", "async-trait", @@ -2160,7 +2250,7 @@ dependencies = [ "cfg-if", "encoding_rs", "fxprof-processed-profile", - "indexmap 2.0.0", + "indexmap 2.2.3", "libc", "log", "object", @@ -2172,7 +2262,7 @@ dependencies = [ "serde_json", "target-lexicon", "wasm-encoder 0.38.1", - "wasmparser 0.118.1", + "wasmparser 0.118.2", "wasmtime-cache", "wasmtime-component-macro", "wasmtime-component-util", @@ -2183,64 +2273,64 @@ dependencies = [ "wasmtime-runtime", "wasmtime-winch", "wat", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "wasmtime-asm-macros" -version = "16.0.0" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "660ba9143e15a2acd921820df221b73aee256bd3ca2d208d73d8adc9587ccbb9" +checksum = "c1495ef4d46aec14f967b672e946e391dd8a14a443cda3d5e0779ff67fb6e28d" dependencies = [ "cfg-if", ] [[package]] name = "wasmtime-cache" -version = "16.0.0" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3ce373743892002f9391c6741ef0cb0335b55ec899d874f311222b7e36f4594" +checksum = "e2de1b065bdbaca3df9e7e9f70eb129e326a99d971b16d666acd798d98d47635" dependencies = [ "anyhow", "base64", "bincode", "directories-next", "log", - "rustix 0.38.30", + "rustix 0.38.31", "serde", "serde_derive", "sha2", "toml 0.5.11", - "windows-sys 0.48.0", + "windows-sys 0.52.0", "zstd", ] [[package]] name = "wasmtime-component-macro" -version = "16.0.0" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12ef32643324e564e1c359e9044daa06cbf90d7e2d6c99a738d17a12959f01a5" +checksum = "2f19bcff82f81ba0273c0b68f3909977b0dd54489bc86c630d8aad43dca92f3f" dependencies = [ "anyhow", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.48", "wasmtime-component-util", "wasmtime-wit-bindgen", - "wit-parser", + "wit-parser 0.13.2", ] [[package]] name = "wasmtime-component-util" -version = "16.0.0" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c87d06c18d21a4818f354c00a85f4ebc62b2270961cd022968452b0e4dbed9d" +checksum = "8af072b7ad5ac5583e1f9e4737ebf88923de564fb5d4ace0ca9b4b720bdf95a1" [[package]] name = "wasmtime-cranelift" -version = "16.0.0" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d648c8b4064a7911093b02237cd5569f71ca171d3a0a486bf80600b19e1cba2" +checksum = "df08a8bd9a68732577bee05ac685e4c247238b5e79ad9c062e2dfb4d04dca132" dependencies = [ "anyhow", "cfg-if", @@ -2250,12 +2340,12 @@ dependencies = [ "cranelift-frontend", "cranelift-native", "cranelift-wasm", - "gimli 0.28.0", + "gimli 0.28.1", "log", "object", "target-lexicon", "thiserror", - "wasmparser 0.118.1", + "wasmparser 0.118.2", "wasmtime-cranelift-shared", "wasmtime-environ", "wasmtime-versioned-export-macros", @@ -2263,15 +2353,15 @@ dependencies = [ [[package]] name = "wasmtime-cranelift-shared" -version = "16.0.0" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290a89027688782da8ff60b12bb95695494b1874e0d0ba2ba387d23dace6d70c" +checksum = "404201c9e669083f189f01337b3ed0aa0eb081157fb4e170bbfe193df9497771" dependencies = [ "anyhow", "cranelift-codegen", "cranelift-control", "cranelift-native", - "gimli 0.28.0", + "gimli 0.28.1", "object", "target-lexicon", "wasmtime-environ", @@ -2279,14 +2369,14 @@ dependencies = [ [[package]] name = "wasmtime-environ" -version = "16.0.0" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61eb64fb3e0da883e2df4a13a81d6282e072336e6cb6295021d0f7ab2e352754" +checksum = "7e696b4911c9a69c3c2892ec05eb41bb15436d1a46d8830a755c40f5df47546a" dependencies = [ "anyhow", "cranelift-entity", - "gimli 0.28.0", - "indexmap 2.0.0", + "gimli 0.28.1", + "indexmap 2.2.3", "log", "object", "serde", @@ -2294,7 +2384,7 @@ dependencies = [ "target-lexicon", "thiserror", "wasm-encoder 0.38.1", - "wasmparser 0.118.1", + "wasmparser 0.118.2", "wasmprinter", "wasmtime-component-util", "wasmtime-types", @@ -2302,36 +2392,36 @@ dependencies = [ [[package]] name = "wasmtime-fiber" -version = "16.0.0" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecf1d3a838b0956b71ad3f8cb80069a228339775bf02dd35d86a5a68bbe443" +checksum = "4a39681c1f6f54d1bf7efe5dc829f8d7fc0e2ca12c346fd7a3efbf726e9681d2" dependencies = [ "anyhow", "cc", "cfg-if", - "rustix 0.38.30", + "rustix 0.38.31", "wasmtime-asm-macros", "wasmtime-versioned-export-macros", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "wasmtime-jit" -version = "16.0.0" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f485336add49267d8859e8f8084d2d4b9a4b1564496b6f30ba5b168d50c10ceb" +checksum = "2c56519882d936c680bd191d58ac04cff071a470eca2dcc664adcd60f986a731" dependencies = [ "addr2line", "anyhow", "bincode", "cfg-if", "cpp_demangle", - "gimli 0.28.0", + "gimli 0.28.1", "ittapi", "log", "object", "rustc-demangle", - "rustix 0.38.30", + "rustix 0.38.31", "serde", "serde_derive", "target-lexicon", @@ -2339,43 +2429,43 @@ dependencies = [ "wasmtime-jit-debug", "wasmtime-jit-icache-coherence", "wasmtime-runtime", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "wasmtime-jit-debug" -version = "16.0.0" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e119affec40edb2fab9044f188759a00c2df9c3017278d047012a2de1efb4f" +checksum = "babc65e64ab0dd4e1ce65624db64e24ed0fbdebb16148729173fa0da9f70e53c" dependencies = [ "object", "once_cell", - "rustix 0.38.30", + "rustix 0.38.31", "wasmtime-versioned-export-macros", ] [[package]] name = "wasmtime-jit-icache-coherence" -version = "16.0.0" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b6d197fcc34ad32ed440e1f9552fd57d1f377d9699d31dee1b5b457322c1f8a" +checksum = "d7ec5b11c12d9acb09612e7ce04c4c8aea3e8dc79b2591ffdead986a5ce8ec49" dependencies = [ "cfg-if", "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "wasmtime-runtime" -version = "16.0.0" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "794b2bb19b99ef8322ff0dd9fe1ba7e19c41036dfb260b3f99ecce128c42ff92" +checksum = "28e1c31bbdf67cb86f149bcead5193749f23f77c93c5244ec9ac8d192f90966c" dependencies = [ "anyhow", "cc", "cfg-if", "encoding_rs", - "indexmap 2.0.0", + "indexmap 2.2.3", "libc", "log", "mach", @@ -2383,7 +2473,7 @@ dependencies = [ "memoffset", "paste", "psm", - "rustix 0.38.30", + "rustix 0.38.31", "sptr", "wasm-encoder 0.38.1", "wasmtime-asm-macros", @@ -2392,38 +2482,38 @@ dependencies = [ "wasmtime-jit-debug", "wasmtime-versioned-export-macros", "wasmtime-wmemcheck", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "wasmtime-types" -version = "16.0.0" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d995db8bb56f2cd8d2dc0ed5ffab94ffb435283b0fe6747f80f7aab40b2d06a1" +checksum = "52e799cff634d30fd042db96b417d515e54f903b95f8c1e0ec60e8f604479485" dependencies = [ "cranelift-entity", "serde", "serde_derive", "thiserror", - "wasmparser 0.118.1", + "wasmparser 0.118.2", ] [[package]] name = "wasmtime-versioned-export-macros" -version = "16.0.0" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c5565959287c21dd0f4277ae3518dd2ae62679f655ee2dbc4396e19d210db" +checksum = "e10fe166d4e4c95d5d80c5b47e1e12256af2099ac525ddb9a19b1aeb8896e5e1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.48", ] [[package]] name = "wasmtime-wasi" -version = "16.0.0" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccd8370078149d49a3a47e93741553fd79b700421464b6a27ca32718192ab130" +checksum = "494f99111a165dcddc69aaa5fa23604f49dcfab479a869edd84581abd6ac569b" dependencies = [ "anyhow", "async-trait", @@ -2432,16 +2522,16 @@ dependencies = [ "cap-fs-ext", "cap-net-ext", "cap-rand", - "cap-std 2.0.0", + "cap-std 2.0.1", "cap-time-ext", - "fs-set-times 0.20.0", + "fs-set-times 0.20.1", "futures", - "io-extras 0.18.0", - "io-lifetimes 2.0.2", + "io-extras 0.18.1", + "io-lifetimes 2.0.3", "libc", "log", "once_cell", - "rustix 0.38.30", + "rustix 0.38.31", "system-interface", "thiserror", "tokio", @@ -2451,21 +2541,21 @@ dependencies = [ "wasi-common", "wasmtime", "wiggle", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "wasmtime-winch" -version = "16.0.0" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6f945ff9bad96e0a69973d74f193c19f627c8adbf250e7cb73ae7564b6cc8a" +checksum = "d3f5d76d399cb4423e6f178bc154a0e1c314711e28dabaa6e757e56628a083ec" dependencies = [ "anyhow", "cranelift-codegen", - "gimli 0.28.0", + "gimli 0.28.1", "object", "target-lexicon", - "wasmparser 0.118.1", + "wasmparser 0.118.2", "wasmtime-cranelift-shared", "wasmtime-environ", "winch-codegen", @@ -2473,21 +2563,21 @@ dependencies = [ [[package]] name = "wasmtime-wit-bindgen" -version = "16.0.0" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f328b2d4a690270324756e886ed5be3a4da4c00be0eea48253f4595ad068062b" +checksum = "6bb3bc92c031cf4961135bffe055a69c1bd67c253dca20631478189bb05ec27b" dependencies = [ "anyhow", "heck 0.4.1", - "indexmap 2.0.0", - "wit-parser", + "indexmap 2.2.3", + "wit-parser 0.13.2", ] [[package]] name = "wasmtime-wmemcheck" -version = "16.0.0" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67761d8f8c0b3c13a5d34356274b10a40baba67fe9cfabbfc379a8b414e45de2" +checksum = "5da08ab734954e16f57be38423b90c25a0b13420e51cbd0a2e37b86a468a988c" [[package]] name = "wast" @@ -2500,30 +2590,31 @@ dependencies = [ [[package]] name = "wast" -version = "70.0.0" +version = "71.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee4bc54bbe1c6924160b9f75e374a1d07532e7580eb632c0ee6cdd109bb217e" +checksum = "647c3ac4354da32688537e8fc4d2fe6c578df51896298cb64727d98088a1fd26" dependencies = [ + "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder 0.39.0", + "wasm-encoder 0.41.2", ] [[package]] name = "wat" -version = "1.0.83" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f0dce8cdc288c717cf01e461a1e451a7b8445d53451123536ba576e423a101a" +checksum = "b69c36f634411568a2c6d24828b674961e37ea03340fe1d605c337ed8162d901" dependencies = [ - "wast 70.0.0", + "wast 71.0.1", ] [[package]] name = "wiggle" -version = "16.0.0" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0afb26cd3269289bb314a361ff0a6685e5ce793b62181a9fe3f81ace15051697" +checksum = "cd5b200b5dd3d5d7cc4093166f4f916d2d2839296cf1b1757b9726635f6425c3" dependencies = [ "anyhow", "async-trait", @@ -2536,28 +2627,28 @@ dependencies = [ [[package]] name = "wiggle-generate" -version = "16.0.0" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cef2868fed7584d2b552fa317104858ded80021d23b073b2d682d3c932a027bd" +checksum = "a4dc34a2bc1091599de005e9b854cd1a9ea35b16ca51cac2797274c1a2666e06" dependencies = [ "anyhow", "heck 0.4.1", "proc-macro2", "quote", "shellexpand", - "syn 2.0.37", + "syn 2.0.48", "witx", ] [[package]] name = "wiggle-macro" -version = "16.0.0" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31ae1ec11a17ea481539ee9a5719a278c9790d974060fbf71db4b2c05378780b" +checksum = "37ba3b37f402a7513b9ed7973a6e907074987b3afdcede98d3d79939b3e76f1b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.48", "wiggle-generate", ] @@ -2594,20 +2685,29 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "winch-codegen" -version = "0.14.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58e58c236a6abdd9ab454552b4f29e16cfa837a86897c1503313b2e62e7609ec" +checksum = "8d921185084e134e897e0e202e129a422306d0f1391954ecf4928d36defa897d" dependencies = [ "anyhow", "cranelift-codegen", - "gimli 0.28.0", + "gimli 0.28.1", "regalloc2", "smallvec", "target-lexicon", - "wasmparser 0.118.1", + "wasmparser 0.118.2", "wasmtime-environ", ] +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -2742,9 +2842,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.15" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" +checksum = "6b1dbce9e90e5404c5a52ed82b1d13fc8cfbdad85033b6f57546ffd1265f8451" dependencies = [ "memchr", ] @@ -2762,19 +2862,19 @@ dependencies = [ [[package]] name = "winx" -version = "0.36.2" +version = "0.36.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357bb8e2932df531f83b052264b050b81ba0df90ee5a59b2d1d3949f344f81e5" +checksum = "f9643b83820c0cd246ecabe5fa454dd04ba4fa67996369466d0747472d337346" dependencies = [ "bitflags 2.4.2", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "wit-bindgen" -version = "0.16.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b76f1d099678b4f69402a421e888bbe71bf20320c2f3f3565d0e7484dbe5bc20" +checksum = "d78a762c4fbc1bc0dedf4aaa280ac5ea87e0d3a7ec83487b7b25999a55ec4b73" dependencies = [ "bitflags 2.4.2", "wit-bindgen-rust-macro", @@ -2782,96 +2882,95 @@ dependencies = [ [[package]] name = "wit-bindgen-core" -version = "0.16.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75d55e1a488af2981fb0edac80d8d20a51ac36897a1bdef4abde33c29c1b6d0d" +checksum = "43cc97e83ffaabab15791f6fe7d7f8c4e1dc670243e460765c2830463d53bcdf" dependencies = [ "anyhow", - "wit-component 0.18.2", - "wit-parser", + "wit-component", + "wit-parser 0.14.0", ] [[package]] name = "wit-bindgen-rust" -version = "0.16.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01ff9cae7bf5736750d94d91eb8a49f5e3a04aff1d1a3218287d9b2964510f8" +checksum = "a67b2ee0578e3e8be67f1d863b86e5d649bb16d56c85752eef5228967db103e2" dependencies = [ "anyhow", "heck 0.4.1", "wasm-metadata", "wit-bindgen-core", - "wit-component 0.18.2", + "wit-component", ] [[package]] name = "wit-bindgen-rust-macro" -version = "0.16.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804a98e2538393d47aa7da65a7348116d6ff403b426665152b70a168c0146d49" +checksum = "345a07e6740bc71c7da6e95e9547d89c54895b8a93fad9d192b67c1ec8358e32" dependencies = [ "anyhow", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.48", "wit-bindgen-core", "wit-bindgen-rust", - "wit-component 0.18.2", + "wit-component", ] [[package]] name = "wit-component" -version = "0.18.2" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a35a2a9992898c9d27f1664001860595a4bc99d32dd3599d547412e17d7e2" +checksum = "be60cd1b2ff7919305301d0c27528d4867bd793afe890ba3837743da9655d91b" dependencies = [ "anyhow", "bitflags 2.4.2", - "indexmap 2.0.0", + "indexmap 2.2.3", "log", "serde", "serde_derive", "serde_json", - "wasm-encoder 0.38.1", + "wasm-encoder 0.41.2", "wasm-metadata", - "wasmparser 0.118.1", - "wit-parser", + "wasmparser 0.121.2", + "wit-parser 0.14.0", ] [[package]] -name = "wit-component" -version = "0.19.1" +name = "wit-parser" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "429e3c06fba3a7566aab724ae3ffff3152ede5399d44789e7dd11f5421292859" +checksum = "316b36a9f0005f5aa4b03c39bc3728d045df136f8c13a73b7db4510dec725e08" dependencies = [ "anyhow", - "bitflags 2.4.2", - "indexmap 2.0.0", + "id-arena", + "indexmap 2.2.3", "log", + "semver", "serde", "serde_derive", "serde_json", - "wasm-encoder 0.39.0", - "wasm-metadata", - "wasmparser 0.119.0", - "wit-parser", + "unicode-xid", ] [[package]] name = "wit-parser" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df4913a2219096373fd6512adead1fb77ecdaa59d7fc517972a7d30b12f625be" +checksum = "1ee4ad7310367bf272507c0c8e0c74a80b4ed586b833f7c7ca0b7588f686f11a" dependencies = [ "anyhow", "id-arena", - "indexmap 2.0.0", + "indexmap 2.2.3", "log", "semver", "serde", "serde_derive", "serde_json", "unicode-xid", + "wasmparser 0.121.2", ] [[package]] @@ -2886,6 +2985,26 @@ dependencies = [ "wast 35.0.2", ] +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "zstd" version = "0.11.2+zstd.1.5.2" @@ -2907,11 +3026,10 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.8+zstd.1.5.5" +version = "2.0.9+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" dependencies = [ "cc", - "libc", "pkg-config", ] diff --git a/Cargo.toml b/Cargo.toml index 78eae1e..001428b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,12 +32,12 @@ rustflags = ["-Zoom=panic"] anyhow = "1" clap = { version = "4", features = ["derive"] } serde = { version = "1", features = ["derive"] } -toml = "0.7" +toml = "0.8" walrus = "0.20.3" -wasm-compose = "0.5.0" -wasm-metadata = "0.10.15" -wasm-opt = "0.114.1" -wit-component = "0.19.1" +wasm-compose = "0.5.5" +wasm-metadata = "0.10.20" +wasm-opt = "0.114.2" +wit-component = "0.21.0" [build-dependencies] anyhow = "1" @@ -47,10 +47,10 @@ anyhow = "1" cap-std = "1.0.12" heck = { version = "0.4" } tokio = { version = "1.30.0", features = ["macros"] } -wasmtime = { version = "16.0.0", features = ["component-model"] } -wasmtime-wasi = "16.0.0" -wasmparser = "0.119.0" +wasmtime = { version = "17.0.1", features = ["component-model"] } +wasmtime-wasi = "17.0.1" +wasmparser = "0.121.2" [workspace.dependencies] anyhow = "1" -wit-bindgen = "0.16.0" +wit-bindgen = "0.17.1" diff --git a/build-adapter.sh b/build-adapter.sh index 362ae42..5ee0c63 100755 --- a/build-adapter.sh +++ b/build-adapter.sh @@ -1,2 +1,11 @@ -cargo +nightly build -p virtual-adapter --target wasm32-wasi --release -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort && cp target/wasm32-wasi/release/virtual_adapter.wasm lib/ -cargo +nightly build -p virtual-adapter --target wasm32-wasi --release --features debug -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort && cp target/wasm32-wasi/release/virtual_adapter.wasm lib/virtual_adapter.debug.wasm +# Useful for debugging: +# export CARGO_PROFILE_RELEASE_DEBUG=2 +# export WIT_BINDGEN_DEBUG=1 + +wasm-tools component wit --wasm wit -o lib/package.wasm + +cargo build -p virtual-adapter --target wasm32-unknown-unknown --release -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort && + cp target/wasm32-unknown-unknown/release/virtual_adapter.wasm lib/virtual_adapter.wasm + +cargo build -p virtual-adapter --target wasm32-unknown-unknown --release --features debug -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort && + cp target/wasm32-unknown-unknown/release/virtual_adapter.wasm lib/virtual_adapter.debug.wasm diff --git a/lib/virtual_adapter.debug.wasm b/lib/virtual_adapter.debug.wasm index 5daea843d0c5cfad9fa475e669af35d76285a313..1e78cf54e5e3c87e94e1f77c7f3405dcd6b77a86 100755 GIT binary patch literal 230108 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

^bdU@a`E6k+&Qz4bCZW84>{0OVEzTNw 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 literal 258561 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 diff --git a/lib/virtual_adapter.wasm b/lib/virtual_adapter.wasm index 61cc257e53f16ca9c6859cea2f15a635e248e227..6573579a0bc3c0afdbfd735dc301a4bcdd30e409 100755 GIT binary patch literal 194283 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 N{$Wf&djmoJ{{Uy$$qE1f literal 226946 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 xu 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π(Aku-0~DBKn`ryZXF@q47#Hi{vF;YP)a*o3pR| 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 kYmJv5f!lYy#WpFsUGDS$1>OGz`v3p{ diff --git a/lib/wasi_snapshot_preview1.reactor.wasm b/lib/wasi_snapshot_preview1.reactor.wasm index 06107038a811938a98ff127a0bb4b03364b78dab..82455c803b3034c775a6d3e3cfec223a8fc375c7 100644 GIT binary patch delta 19052 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 diff --git a/src/lib.rs b/src/lib.rs index 87da4e0..2ecc0db 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,13 +5,11 @@ use virt_deny::{ deny_clocks_virt, deny_exit_virt, deny_http_virt, deny_random_virt, deny_sockets_virt, }; use virt_env::{create_env_virt, strip_env_virt}; -use virt_io::{ - create_io_virt, strip_clocks_virt, strip_fs_virt, strip_http_virt, strip_io_virt, - strip_sockets_virt, strip_stdio_virt, VirtStdio, -}; +use virt_io::{create_io_virt, VirtStdio}; +use walrus_ops::strip_virt; use wasm_metadata::Producers; -use wasm_opt::{Feature, OptimizationOptions}; -use wit_component::{metadata, ComponentEncoder, StringEncoding}; +use wasm_opt::{Feature, OptimizationOptions, ShrinkLevel}; +use wit_component::{metadata, ComponentEncoder, DecodedWasm, StringEncoding}; mod data; mod stub_preview1; @@ -26,6 +24,7 @@ pub use virt_io::{FsEntry, StdioCfg, VirtFs, VirtualFiles}; const VIRT_ADAPTER: &[u8] = include_bytes!("../lib/virtual_adapter.wasm"); const VIRT_ADAPTER_DEBUG: &[u8] = include_bytes!("../lib/virtual_adapter.debug.wasm"); +const VIRT_WIT_METADATA: &[u8] = include_bytes!("../lib/package.wasm"); /// Virtualization options /// @@ -131,7 +130,10 @@ impl WasiVirt { } pub fn finish(&mut self) -> Result { - let config = walrus::ModuleConfig::new(); + let mut config = walrus::ModuleConfig::new(); + if self.debug { + config.generate_dwarf(true); + } let mut module = if self.debug { config.parse(VIRT_ADAPTER_DEBUG) } else { @@ -164,70 +166,45 @@ impl WasiVirt { .remove_raw("component-type:virtual-adapter") .context("Unable to find component section")?; - let (_, mut bindgen) = if self.debug { - metadata::decode(VIRT_ADAPTER_DEBUG) - } else { - metadata::decode(VIRT_ADAPTER) - }?; - let (_, pkg_id) = bindgen - .resolve - .package_names - .iter() - .find(|(name, _)| name.namespace == "local") - .unwrap(); - - let base_world = bindgen - .resolve - .select_world(*pkg_id, Some("virtual-base"))?; - - let env_world = bindgen.resolve.select_world(*pkg_id, Some("virtual-env"))?; - - let io_world = bindgen.resolve.select_world(*pkg_id, Some("virtual-io"))?; - let io_clocks_world = bindgen - .resolve - .select_world(*pkg_id, Some("virtual-io-clocks"))?; - let io_http_world = bindgen - .resolve - .select_world(*pkg_id, Some("virtual-io-http"))?; - let io_sockets_world = bindgen - .resolve - .select_world(*pkg_id, Some("virtual-io-sockets"))?; - - let exit_world = bindgen - .resolve - .select_world(*pkg_id, Some("virtual-exit"))?; - let fs_world = bindgen.resolve.select_world(*pkg_id, Some("virtual-fs"))?; - let random_world = bindgen - .resolve - .select_world(*pkg_id, Some("virtual-random"))?; - let stdio_world = bindgen - .resolve - .select_world(*pkg_id, Some("virtual-stdio"))?; - let clocks_world = bindgen - .resolve - .select_world(*pkg_id, Some("virtual-clocks"))?; - let http_world = bindgen - .resolve - .select_world(*pkg_id, Some("virtual-http"))?; - let sockets_world = bindgen - .resolve - .select_world(*pkg_id, Some("virtual-sockets"))?; + let (mut resolve, pkg_id) = match wit_component::decode(VIRT_WIT_METADATA)? { + DecodedWasm::WitPackage(resolve, pkg_id) => (resolve, pkg_id), + DecodedWasm::Component(..) => { + anyhow::bail!("expected a WIT package, found a component") + } + }; + + let base_world = resolve.select_world(pkg_id, Some("virtual-base"))?; + + let env_world = resolve.select_world(pkg_id, Some("virtual-env"))?; + + let io_world = resolve.select_world(pkg_id, Some("virtual-io"))?; + let io_clocks_world = resolve.select_world(pkg_id, Some("virtual-io-clocks"))?; + let io_http_world = resolve.select_world(pkg_id, Some("virtual-io-http"))?; + let io_sockets_world = resolve.select_world(pkg_id, Some("virtual-io-sockets"))?; + + let exit_world = resolve.select_world(pkg_id, Some("virtual-exit"))?; + let fs_world = resolve.select_world(pkg_id, Some("virtual-fs"))?; + let random_world = resolve.select_world(pkg_id, Some("virtual-random"))?; + let stdio_world = resolve.select_world(pkg_id, Some("virtual-stdio"))?; + let clocks_world = resolve.select_world(pkg_id, Some("virtual-clocks"))?; + let http_world = resolve.select_world(pkg_id, Some("virtual-http"))?; + let sockets_world = resolve.select_world(pkg_id, Some("virtual-sockets"))?; // env, exit & random subsystems are fully independent if self.env.is_some() { - bindgen.resolve.merge_worlds(env_world, base_world)?; + resolve.merge_worlds(env_world, base_world)?; } else { strip_env_virt(&mut module)?; } if let Some(exit) = self.exit { if !exit { - bindgen.resolve.merge_worlds(exit_world, base_world)?; + resolve.merge_worlds(exit_world, base_world)?; deny_exit_virt(&mut module)?; } } if let Some(random) = self.random { if !random { - bindgen.resolve.merge_worlds(random_world, base_world)?; + resolve.merge_worlds(random_world, base_world)?; deny_random_virt(&mut module)?; } } @@ -235,69 +212,64 @@ impl WasiVirt { // io subsystems have io dependence due to streams + poll // therefore we need to strip just their io dependence portion if has_io { - bindgen.resolve.merge_worlds(io_world, base_world)?; + resolve.merge_worlds(io_world, base_world)?; } else { - strip_io_virt(&mut module)?; + strip_virt(&mut module, &["wasi:io/"])?; } if let Some(clocks) = self.clocks { if !clocks { // deny is effectively virtualization // in future with fine-grained virtualization options, they // also would extend here (ie !clocks is deceiving) - bindgen.resolve.merge_worlds(clocks_world, base_world)?; + resolve.merge_worlds(clocks_world, base_world)?; deny_clocks_virt(&mut module)?; } else { // passthrough can be simplified to just rewrapping io interfaces - bindgen.resolve.merge_worlds(io_clocks_world, base_world)?; + resolve.merge_worlds(io_clocks_world, base_world)?; } } else { - strip_clocks_virt(&mut module)?; + strip_virt(&mut module, &["wasi:clocks/"])?; } // sockets and http are identical to clocks above if let Some(sockets) = self.sockets { if !sockets { - bindgen.resolve.merge_worlds(sockets_world, base_world)?; + resolve.merge_worlds(sockets_world, base_world)?; deny_sockets_virt(&mut module)?; } else { - bindgen.resolve.merge_worlds(io_sockets_world, base_world)?; + resolve.merge_worlds(io_sockets_world, base_world)?; } } else { - strip_sockets_virt(&mut module)?; + strip_virt(&mut module, &["wasi:sockets/"])?; } if let Some(http) = self.http { if !http { - bindgen.resolve.merge_worlds(http_world, base_world)?; + resolve.merge_worlds(http_world, base_world)?; deny_http_virt(&mut module)?; } else { - bindgen.resolve.merge_worlds(io_http_world, base_world)?; + resolve.merge_worlds(io_http_world, base_world)?; } } else { - strip_http_virt(&mut module)?; + strip_virt(&mut module, &["wasi:http/"])?; } // stdio and fs are fully implemented in io world // (all their interfaces use streams) if self.stdio.is_some() { - bindgen.resolve.merge_worlds(stdio_world, base_world)?; + resolve.merge_worlds(stdio_world, base_world)?; } else { - strip_stdio_virt(&mut module)?; + strip_virt(&mut module, &["wasi:cli/std", "wasi:cli/terminal"])?; } if self.fs.is_some() || self.stdio.is_some() { - bindgen.resolve.merge_worlds(fs_world, base_world)?; + resolve.merge_worlds(fs_world, base_world)?; } else { - strip_fs_virt(&mut module)?; + strip_virt(&mut module, &["wasi:filesystem/"])?; } let mut producers = Producers::default(); producers.add("processed-by", "wasi-virt", env!("CARGO_PKG_VERSION")); - component_section.data = metadata::encode( - &bindgen.resolve, - base_world, - StringEncoding::UTF8, - Some(&producers), - None, - )?; + component_section.data = + metadata::encode(&resolve, base_world, StringEncoding::UTF8, Some(&producers))?; module.customs.add(component_section); @@ -312,8 +284,9 @@ impl WasiVirt { let tmp_output = dir.join(format!("virt.core.output.{}.wasm", timestamp())); fs::write(&tmp_input, bytes) .with_context(|| "Unable to write temporary file for wasm-opt call on adapter")?; - OptimizationOptions::new_optimize_for_size_aggressively() - .enable_feature(Feature::ReferenceTypes) + OptimizationOptions::new_opt_level_2() + .shrink_level(ShrinkLevel::Level1) + .enable_feature(Feature::All) .run(&tmp_input, &tmp_output) .with_context(|| "Unable to apply wasm-opt optimization to virt. This can be disabled with wasm_opt: false.") .or_else(|e| { diff --git a/src/virt_deny/clocks.rs b/src/virt_deny/clocks.rs index 494ca8d..8e7224a 100644 --- a/src/virt_deny/clocks.rs +++ b/src/virt_deny/clocks.rs @@ -3,7 +3,7 @@ use std::sync::OnceLock; use anyhow::Result; use walrus::{FuncParams, FuncResults, Module, ValType}; -use crate::virt_io::stub_clocks_virt; +use crate::walrus_ops::stub_virt; use super::replace_or_insert_stub_for_exports; @@ -15,32 +15,32 @@ fn get_wasi_clock_fns() -> &'static Vec<(&'static str, FuncParams, FuncResults)> WASI_CLOCK_FNS.get_or_init(|| { Vec::from([ ( - "wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10#now", + "wasi:clocks/monotonic-clock@0.2.0#now", vec![], vec![ValType::I64], ), ( - "wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10#resolution", + "wasi:clocks/monotonic-clock@0.2.0#resolution", vec![], vec![ValType::I64], ), ( - "wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10#subscribe-instant", + "wasi:clocks/monotonic-clock@0.2.0#subscribe-instant", vec![ValType::I64], vec![ValType::I32], ), ( - "wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10#subscribe-duration", + "wasi:clocks/monotonic-clock@0.2.0#subscribe-duration", vec![ValType::I64], vec![ValType::I32], ), ( - "wasi:clocks/wall-clock@0.2.0-rc-2023-11-10#now", + "wasi:clocks/wall-clock@0.2.0#now", vec![], vec![ValType::I32], ), ( - "wasi:clocks/wall-clock@0.2.0-rc-2023-11-10#resolution", + "wasi:clocks/wall-clock@0.2.0#resolution", vec![], vec![ValType::I32], ), @@ -50,6 +50,6 @@ fn get_wasi_clock_fns() -> &'static Vec<(&'static str, FuncParams, FuncResults)> /// Replace exports related to clocks in WASI to deny access pub(crate) fn deny_clocks_virt(module: &mut Module) -> Result<()> { - stub_clocks_virt(module)?; + stub_virt(module, &["wasi:clocks/"], false)?; replace_or_insert_stub_for_exports(module, get_wasi_clock_fns()) } diff --git a/src/virt_deny/exit.rs b/src/virt_deny/exit.rs index a79c6f5..a135c5e 100644 --- a/src/virt_deny/exit.rs +++ b/src/virt_deny/exit.rs @@ -10,13 +10,8 @@ static WASI_EXIT_FNS: OnceLock> = OnceLock: /// Retrieve or initialize the static list of functions related to exiting in WASI fn get_wasi_exit_fns() -> &'static Vec<(&'static str, FuncParams, FuncResults)> { - WASI_EXIT_FNS.get_or_init(|| { - Vec::from([( - "wasi:cli/exit@0.2.0-rc-2023-12-05#exit", - vec![ValType::I32], - vec![], - )]) - }) + WASI_EXIT_FNS + .get_or_init(|| Vec::from([("wasi:cli/exit@0.2.0#exit", vec![ValType::I32], vec![])])) } /// Replace exports related to exiting in WASI to deny access diff --git a/src/virt_deny/http.rs b/src/virt_deny/http.rs index 7ca8bb4..f9340ff 100644 --- a/src/virt_deny/http.rs +++ b/src/virt_deny/http.rs @@ -3,7 +3,7 @@ use std::sync::OnceLock; use anyhow::Result; use walrus::{FuncParams, FuncResults, Module, ValType}; -use crate::virt_io::stub_http_virt; +use crate::walrus_ops::stub_virt; use super::replace_or_insert_stub_for_exports; @@ -15,12 +15,12 @@ fn get_wasi_http_fns() -> &'static Vec<(&'static str, FuncParams, FuncResults)> WASI_HTTP_FNS.get_or_init(|| { Vec::from([ ( - "wasi:http/incoming-handler@0.2.0-rc-2023-10-18#handle", + "wasi:http/incoming-handler@0.2.0#handle", vec![ValType::I32, ValType::I32], vec![], ), ( - "wasi:http/outgoing-handler@0.2.0-rc-2023-10-18#handle", + "wasi:http/outgoing-handler@0.2.0#handle", vec![ ValType::I32, ValType::I32, @@ -34,22 +34,32 @@ fn get_wasi_http_fns() -> &'static Vec<(&'static str, FuncParams, FuncResults)> vec![ValType::I32], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[dtor]fields", + "wasi:http/types@0.2.0#[dtor]fields", vec![ValType::I32], vec![], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[constructor]fields", + "wasi:http/types@0.2.0#[constructor]fields", vec![ValType::I32, ValType::I32], vec![ValType::I32], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[method]fields.get", + "wasi:http/types@0.2.0#[constructor]fields.from-list", + vec![ValType::I32, ValType::I32], + vec![ValType::I32], + ), + ( + "wasi:http/types@0.2.0#[method]fields.get", vec![ValType::I32, ValType::I32, ValType::I32], vec![ValType::I32], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[method]fields.set", + "wasi:http/types@0.2.0#[method]fields.has", + vec![ValType::I32, ValType::I32, ValType::I32], + vec![ValType::I32], + ), + ( + "wasi:http/types@0.2.0#[method]fields.set", vec![ ValType::I32, ValType::I32, @@ -60,12 +70,12 @@ fn get_wasi_http_fns() -> &'static Vec<(&'static str, FuncParams, FuncResults)> vec![], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[method]fields.delete", + "wasi:http/types@0.2.0#[method]fields.delete", vec![ValType::I32, ValType::I32, ValType::I32], vec![], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[method]fields.append", + "wasi:http/types@0.2.0#[method]fields.append", vec![ ValType::I32, ValType::I32, @@ -76,117 +86,162 @@ fn get_wasi_http_fns() -> &'static Vec<(&'static str, FuncParams, FuncResults)> vec![], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[method]fields.entries", + "wasi:http/types@0.2.0#[method]fields.entries", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[method]fields.clone", + "wasi:http/types@0.2.0#[method]fields.clone", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[dtor]incoming-body", + "wasi:http/types@0.2.0#[dtor]incoming-request", vec![ValType::I32], vec![], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[method]incoming-body.stream", + "wasi:http/types@0.2.0#[method]incoming-request.method", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[static]incoming-body.finish", + "wasi:http/types@0.2.0#[method]incoming-request.path-with-query", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[dtor]outgoing-body", + "wasi:http/types@0.2.0#[method]incoming-request.scheme", + vec![ValType::I32], vec![ValType::I32], - vec![], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[method]outgoing-body.write", + "wasi:http/types@0.2.0#[method]incoming-request.authority", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[static]outgoing-body.finish", - vec![ValType::I32, ValType::I32, ValType::I32], - vec![], + "wasi:http/types@0.2.0#[method]incoming-request.headers", + vec![ValType::I32], + vec![ValType::I32], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[dtor]incoming-request", + "wasi:http/types@0.2.0#[method]incoming-request.consume", + vec![ValType::I32], vec![ValType::I32], - vec![], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[dtor]outgoing-request", + "wasi:http/types@0.2.0#[dtor]outgoing-request", vec![ValType::I32], vec![], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[method]incoming-request.method", + "wasi:http/types@0.2.0#[constructor]outgoing-request", + vec![ + ValType::I32, + ValType::I32, + ValType::I32, + ValType::I32, + ValType::I32, + ValType::I32, + ValType::I32, + ValType::I32, + ValType::I32, + ValType::I32, + ValType::I32, + ValType::I32, + ValType::I32, + ValType::I32, + ], + vec![ValType::I32], + ), + ( + "wasi:http/types@0.2.0#[method]outgoing-request.body", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[method]incoming-request.path-with-query", + "wasi:http/types@0.2.0#[method]outgoing-request.method", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[method]incoming-request.scheme", + "wasi:http/types@0.2.0#[method]outgoing-request.set-method", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[method]incoming-request.authority", + "wasi:http/types@0.2.0#[method]outgoing-request.path-with-query", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[method]incoming-request.headers", + "wasi:http/types@0.2.0#[method]outgoing-request.set-path-with-query", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[method]incoming-request.consume", + "wasi:http/types@0.2.0#[method]outgoing-request.scheme", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[constructor]outgoing-request", - vec![ - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ], + "wasi:http/types@0.2.0#[method]outgoing-request.set-scheme", + vec![ValType::I32], + vec![ValType::I32], + ), + ( + "wasi:http/types@0.2.0#[method]outgoing-request.authority", + vec![ValType::I32], + vec![ValType::I32], + ), + ( + "wasi:http/types@0.2.0#[method]outgoing-request.set-authority", + vec![ValType::I32], + vec![ValType::I32], + ), + ( + "wasi:http/types@0.2.0#[method]outgoing-request.headers", + vec![ValType::I32], + vec![ValType::I32], + ), + ( + "wasi:http/types@0.2.0#[dtor]incoming-body", + vec![ValType::I32], + vec![], + ), + ( + "wasi:http/types@0.2.0#[method]incoming-body.stream", + vec![ValType::I32], + vec![ValType::I32], + ), + ( + "wasi:http/types@0.2.0#[static]incoming-body.finish", + vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[method]outgoing-request.write", + "wasi:http/types@0.2.0#[dtor]outgoing-body", + vec![ValType::I32], + vec![], + ), + ( + "wasi:http/types@0.2.0#[method]outgoing-body.write", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[dtor]response-outparam", + "wasi:http/types@0.2.0#[static]outgoing-body.finish", + vec![ValType::I32, ValType::I32, ValType::I32], + vec![], + ), + ( + "wasi:http/types@0.2.0#[dtor]response-outparam", vec![ValType::I32], vec![], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[static]response-outparam.set", + "wasi:http/types@0.2.0#[static]response-outparam.set", vec![ ValType::I32, ValType::I32, @@ -197,72 +252,87 @@ fn get_wasi_http_fns() -> &'static Vec<(&'static str, FuncParams, FuncResults)> vec![ValType::I32], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[dtor]incoming-response", + "wasi:http/types@0.2.0#[dtor]incoming-response", vec![ValType::I32], vec![], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[dtor]outgoing-response", + "wasi:http/types@0.2.0#[method]incoming-response.status", + vec![ValType::I32], vec![ValType::I32], - vec![], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[method]incoming-response.status", + "wasi:http/types@0.2.0#[method]incoming-response.headers", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[method]incoming-response.headers", + "wasi:http/types@0.2.0#[method]incoming-response.consume", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[method]incoming-response.consume", - vec![ValType::I32], + "wasi:http/types@0.2.0#[dtor]outgoing-response", vec![ValType::I32], + vec![], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[constructor]outgoing-response", + "wasi:http/types@0.2.0#[constructor]outgoing-response", vec![ValType::I32, ValType::I32], vec![ValType::I32], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[method]outgoing-response.write", + "wasi:http/types@0.2.0#[method]outgoing-response.body", + vec![ValType::I32], + vec![ValType::I32], + ), + ( + "wasi:http/types@0.2.0#[method]outgoing-response.status-code", + vec![ValType::I32], + vec![ValType::I32], + ), + ( + "wasi:http/types@0.2.0#[method]outgoing-response.set-status-code", + vec![ValType::I32], + vec![ValType::I32], + ), + ( + "wasi:http/types@0.2.0#[method]outgoing-response.headers", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[dtor]future-incoming-response", + "wasi:http/types@0.2.0#[dtor]future-incoming-response", vec![ValType::I32], vec![], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[method]future-incoming-response.get", + "wasi:http/types@0.2.0#[method]future-incoming-response.get", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[method]future-incoming-response.subscribe", + "wasi:http/types@0.2.0#[method]future-incoming-response.subscribe", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[dtor]future-trailers", + "wasi:http/types@0.2.0#[dtor]future-trailers", vec![ValType::I32], vec![], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[method]future-trailers.subscribe", + "wasi:http/types@0.2.0#[method]future-trailers.subscribe", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:http/types@0.2.0-rc-2023-10-18#[method]future-trailers.get", + "wasi:http/types@0.2.0#[method]future-trailers.get", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:http/outgoing-handler@0.2.0-rc-2023-10-18#handle", + "wasi:http/outgoing-handler@0.2.0#handle", vec![ValType::I32; 8], vec![ValType::I32], ), @@ -272,6 +342,6 @@ fn get_wasi_http_fns() -> &'static Vec<(&'static str, FuncParams, FuncResults)> /// Replace exports related to HTTP in WASI to deny access pub(crate) fn deny_http_virt(module: &mut Module) -> Result<()> { - stub_http_virt(module)?; + stub_virt(module, &["wasi:http/"], false)?; replace_or_insert_stub_for_exports(module, get_wasi_http_fns()) } diff --git a/src/virt_deny/random.rs b/src/virt_deny/random.rs index c4bdcf8..c73ea4b 100644 --- a/src/virt_deny/random.rs +++ b/src/virt_deny/random.rs @@ -13,37 +13,37 @@ fn get_wasi_random_fns() -> &'static Vec<(&'static str, FuncParams, FuncResults) WASI_RANDOM_FNS.get_or_init(|| { Vec::from([ ( - "wasi:random/random@0.2.0-rc-2023-11-10#get-random-bytes", + "wasi:random/random@0.2.0#get-random-bytes", vec![ValType::I64], vec![ValType::I32], ), ( - "cabi_post_wasi:random/random@0.2.0-rc-2023-11-10#get-random-bytes", + "cabi_post_wasi:random/random@0.2.0#get-random-bytes", vec![ValType::I32], vec![], ), ( - "wasi:random/random@0.2.0-rc-2023-11-10#get-random-u64", + "wasi:random/random@0.2.0#get-random-u64", vec![], vec![ValType::I64], ), ( - "wasi:random/insecure@0.2.0-rc-2023-11-10#get-insecure-random-bytes", + "wasi:random/insecure@0.2.0#get-insecure-random-bytes", vec![ValType::I64], vec![ValType::I32], ), ( - "cabi_post_wasi:random/insecure@0.2.0-rc-2023-11-10#get-insecure-random-bytes", + "cabi_post_wasi:random/insecure@0.2.0#get-insecure-random-bytes", vec![ValType::I32], vec![], ), ( - "wasi:random/insecure@0.2.0-rc-2023-11-10#get-insecure-random-u64", + "wasi:random/insecure@0.2.0#get-insecure-random-u64", vec![], vec![ValType::I64], ), ( - "wasi:random/insecure-seed@0.2.0-rc-2023-11-10#insecure-seed", + "wasi:random/insecure-seed@0.2.0#insecure-seed", vec![], vec![ValType::I32], ), diff --git a/src/virt_deny/sockets.rs b/src/virt_deny/sockets.rs index 87053ea..a18bd3e 100644 --- a/src/virt_deny/sockets.rs +++ b/src/virt_deny/sockets.rs @@ -3,7 +3,7 @@ use std::sync::OnceLock; use anyhow::Result; use walrus::{FuncParams, FuncResults, Module, ValType}; -use crate::virt_io::stub_sockets_virt; +use crate::walrus_ops::stub_virt; use super::replace_or_insert_stub_for_exports; @@ -15,49 +15,42 @@ pub fn get_wasi_sockets_fns() -> &'static Vec<(&'static str, FuncParams, FuncRes WASI_SOCKETS_FNS.get_or_init(|| { Vec::from([ ( - "wasi:sockets/network@0.2.0-rc-2023-10-18#drop-network", + "wasi:sockets/network@0.2.0#drop-network", vec![ValType::I32], vec![], ), ( - "wasi:sockets/instance-network@0.2.0-rc-2023-10-18#instance-network", + "wasi:sockets/instance-network@0.2.0#instance-network", vec![], vec![ValType::I32], ), ( - "wasi:sockets/ip-name-lookup@0.2.0-rc-2023-10-18#resolve-addresses", - vec![ - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ], + "wasi:sockets/ip-name-lookup@0.2.0#resolve-addresses", + vec![ValType::I32, ValType::I32, ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/ip-name-lookup@0.2.0-rc-2023-10-18#resolve-next-address", + "wasi:sockets/ip-name-lookup@0.2.0#resolve-next-address", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/ip-name-lookup@0.2.0-rc-2023-10-18#drop-resolve-address-stream", + "wasi:sockets/ip-name-lookup@0.2.0#drop-resolve-address-stream", vec![ValType::I32], vec![], ), ( - "wasi:sockets/ip-name-lookup@0.2.0-rc-2023-10-18#subscribe", + "wasi:sockets/ip-name-lookup@0.2.0#subscribe", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/tcp-create-socket@0.2.0-rc-2023-10-18#create-tcp-socket", + "wasi:sockets/tcp-create-socket@0.2.0#create-tcp-socket", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#start-bind", + "wasi:sockets/tcp@0.2.0#start-bind", vec![ ValType::I32, ValType::I32, @@ -77,12 +70,12 @@ pub fn get_wasi_sockets_fns() -> &'static Vec<(&'static str, FuncParams, FuncRes vec![ValType::I32], ), ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#finish-bind", + "wasi:sockets/tcp@0.2.0#finish-bind", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#start-connect", + "wasi:sockets/tcp@0.2.0#start-connect", vec![ ValType::I32, ValType::I32, @@ -102,127 +95,142 @@ pub fn get_wasi_sockets_fns() -> &'static Vec<(&'static str, FuncParams, FuncRes vec![ValType::I32], ), ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#finish-connect", + "wasi:sockets/tcp@0.2.0#finish-connect", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#start-listen", + "wasi:sockets/tcp@0.2.0#start-listen", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#finish-listen", + "wasi:sockets/tcp@0.2.0#finish-listen", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#accept", + "wasi:sockets/tcp@0.2.0#accept", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#local-address", + "wasi:sockets/tcp@0.2.0#local-address", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#remote-address", + "wasi:sockets/tcp@0.2.0#remote-address", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#address-family", + "wasi:sockets/tcp@0.2.0#[method]tcp-socket.is-listening", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#ipv6-only", + "wasi:sockets/tcp@0.2.0#address-family", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#set-ipv6-only", - vec![ValType::I32, ValType::I32], + "wasi:sockets/tcp@0.2.0#set-listen-backlog-size", + vec![ValType::I32, ValType::I64], vec![ValType::I32], ), ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#set-listen-backlog-size", - vec![ValType::I32, ValType::I64], + "wasi:sockets/tcp@0.2.0#keep-alive-enabled", + vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#keep-alive", + "wasi:sockets/tcp@0.2.0#set-keep-alive-enabled", + vec![ValType::I32, ValType::I32], vec![ValType::I32], + ), + ( + "wasi:sockets/tcp@0.2.0#keep-alive-idle-time", + vec![ValType::I32], // fixme vec![ValType::I32], ), ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#set-keep-alive", - vec![ValType::I32, ValType::I32], + "wasi:sockets/tcp@0.2.0#set-keep-alive-idle-time", + vec![ValType::I32, ValType::I32], // fixme vec![ValType::I32], ), ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#no-delay", + "wasi:sockets/tcp@0.2.0#keep-alive-interval", + vec![ValType::I32], // fixme vec![ValType::I32], + ), + ( + "wasi:sockets/tcp@0.2.0#set-keep-alive-interval", + vec![ValType::I32, ValType::I32], // fixme vec![ValType::I32], ), ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#set-no-delay", - vec![ValType::I32, ValType::I32], + "wasi:sockets/tcp@0.2.0#keep-alive-count", + vec![ValType::I32], // fixme + vec![ValType::I32], + ), + ( + "wasi:sockets/tcp@0.2.0#set-keep-alive-count", + vec![ValType::I32, ValType::I32], // fixme vec![ValType::I32], ), ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#unicast-hop-limit", + "wasi:sockets/tcp@0.2.0#hop-limit", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#set-unicast-hop-limit", + "wasi:sockets/tcp@0.2.0#set-hop-limit", vec![ValType::I32, ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#receive-buffer-size", + "wasi:sockets/tcp@0.2.0#receive-buffer-size", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#set-receive-buffer-size", + "wasi:sockets/tcp@0.2.0#set-receive-buffer-size", vec![ValType::I32, ValType::I64], vec![ValType::I32], ), ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#send-buffer-size", + "wasi:sockets/tcp@0.2.0#send-buffer-size", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#set-send-buffer-size", + "wasi:sockets/tcp@0.2.0#set-send-buffer-size", vec![ValType::I32, ValType::I64], vec![ValType::I32], ), ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#subscribe", + "wasi:sockets/tcp@0.2.0#subscribe", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#shutdown", + "wasi:sockets/tcp@0.2.0#shutdown", vec![ValType::I32, ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#drop-tcp-socket", + "wasi:sockets/tcp@0.2.0#drop-tcp-socket", vec![ValType::I32], vec![], ), ( - "wasi:sockets/udp-create-socket@0.2.0-rc-2023-10-18#create-udp-socket", + "wasi:sockets/udp-create-socket@0.2.0#create-udp-socket", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18#start-bind", + "wasi:sockets/udp@0.2.0#start-bind", vec![ ValType::I32, ValType::I32, @@ -242,107 +250,118 @@ pub fn get_wasi_sockets_fns() -> &'static Vec<(&'static str, FuncParams, FuncRes vec![ValType::I32], ), ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18#finish-bind", + "wasi:sockets/udp@0.2.0#finish-bind", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18#start-connect", - vec![ - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ], + "wasi:sockets/udp@0.2.0#local-address", + vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18#finish-connect", + "wasi:sockets/udp@0.2.0#remote-address", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18#receive", - vec![ValType::I32, ValType::I64], + "wasi:sockets/udp@0.2.0#address-family", + vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18#send", - vec![ValType::I32, ValType::I32, ValType::I32], + "wasi:sockets/udp@0.2.0#unicast-hop-limit", + vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18#local-address", - vec![ValType::I32], + "wasi:sockets/udp@0.2.0#set-unicast-hop-limit", + vec![ValType::I32, ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18#remote-address", + "wasi:sockets/udp@0.2.0#receive-buffer-size", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18#address-family", - vec![ValType::I32], + "wasi:sockets/udp@0.2.0#set-receive-buffer-size", + vec![ValType::I32, ValType::I64], vec![ValType::I32], ), ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18#ipv6-only", + "wasi:sockets/udp@0.2.0#send-buffer-size", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18#set-ipv6-only", - vec![ValType::I32, ValType::I32], + "wasi:sockets/udp@0.2.0#set-send-buffer-size", + vec![ValType::I32, ValType::I64], vec![ValType::I32], ), ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18#unicast-hop-limit", + "wasi:sockets/udp@0.2.0#subscribe", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18#set-unicast-hop-limit", - vec![ValType::I32, ValType::I32], + "wasi:sockets/udp@0.2.0#drop-udp-socket", vec![ValType::I32], + vec![], ), ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18#receive-buffer-size", - vec![ValType::I32], - vec![ValType::I32], + "wasi:sockets/udp@0.2.0#[method]udp-socket.stream", + vec![ + ValType::I32, + ValType::I32, + ValType::I32, + ValType::I32, + ValType::I32, + ValType::I32, + ValType::I32, + ValType::I32, + ValType::I32, + ValType::I32, + ValType::I32, + ValType::I32, + ValType::I32, + ValType::I32, + ValType::I32, + ], + vec![], ), ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18#set-receive-buffer-size", - vec![ValType::I32, ValType::I64], - vec![ValType::I32], + "wasi:sockets/udp@0.2.0#[method]incoming-datagram-stream.receive", + vec![ValType::I32, ValType::I64, ValType::I32], + vec![], ), ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18#send-buffer-size", + "wasi:sockets/udp@0.2.0#[method]incoming-datagram-stream.subscribe", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18#set-send-buffer-size", - vec![ValType::I32, ValType::I64], + "wasi:sockets/udp@0.2.0#[resource-drop]incoming-datagram-stream", vec![ValType::I32], + vec![], + ), + ( + "wasi:sockets/udp@0.2.0#[method]outgoing-datagram-stream.check-send", + vec![ValType::I32, ValType::I32], + vec![], + ), + ( + "wasi:sockets/udp@0.2.0#[method]outgoing-datagram-stream.send", + vec![ValType::I32, ValType::I32, ValType::I32, ValType::I32], + vec![], ), ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18#subscribe", + "wasi:sockets/udp@0.2.0#[method]outgoing-datagram-stream.subscribe", vec![ValType::I32], vec![ValType::I32], ), ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18#drop-udp-socket", + "wasi:sockets/udp@0.2.0#[resource-drop]outgoing-datagram-stream", vec![ValType::I32], vec![], ), @@ -352,6 +371,6 @@ pub fn get_wasi_sockets_fns() -> &'static Vec<(&'static str, FuncParams, FuncRes /// Replace exports related to sockets in WASI to deny access pub(crate) fn deny_sockets_virt(module: &mut Module) -> Result<()> { - stub_sockets_virt(module)?; + stub_virt(module, &["wasi:sockets/"], false)?; replace_or_insert_stub_for_exports(module, get_wasi_sockets_fns()) } diff --git a/src/virt_env.rs b/src/virt_env.rs index 491b784..11bb545 100644 --- a/src/virt_env.rs +++ b/src/virt_env.rs @@ -222,7 +222,7 @@ pub(crate) fn stub_env_virt(module: &mut Module) -> Result<()> { module.replace_imported_func( module .imports - .get_func("wasi:cli/environment@0.2.0-rc-2023-12-05", fn_name)?, + .get_func("wasi:cli/environment@0.2.0", fn_name)?, |(body, _)| { body.unreachable(); }, @@ -233,19 +233,19 @@ pub(crate) fn stub_env_virt(module: &mut Module) -> Result<()> { } /// Strip exported functions that implement the WASI CLI environment functionality -/// -/// This function *does not* throw an error if an export does not exist. pub(crate) fn strip_env_virt(module: &mut Module) -> Result<()> { stub_env_virt(module)?; for fn_name in WASI_ENV_FNS { - if let Ok(fid) = module.exports.get_func(format!( - "wasi:cli/environment@0.2.0-rc-2023-12-05#{fn_name}" - )) { - module.replace_exported_func(fid, |(body, _)| { - body.unreachable(); - })?; + let Ok(fid) = module + .exports + .get_func(format!("wasi:cli/environment@0.2.0#{fn_name}")) + else { + bail!("Expected CLI function {fn_name}") }; + module.replace_exported_func(fid, |(body, _)| { + body.unreachable(); + })?; } Ok(()) diff --git a/src/virt_io/mod.rs b/src/virt_io.rs similarity index 93% rename from src/virt_io/mod.rs rename to src/virt_io.rs index c9f3409..53026e8 100644 --- a/src/virt_io/mod.rs +++ b/src/virt_io.rs @@ -7,41 +7,11 @@ use walrus::{ir::Value, ExportItem, GlobalKind, InitExpr, Module}; use crate::{ data::{Data, WasmEncode}, - walrus_ops::{get_active_data_segment, get_stack_global}, + walrus_ops::{get_active_data_segment, get_stack_global, strip_virt, stub_virt}, }; -mod clocks; -mod filesystem; -mod http; -mod io; -mod sockets; -mod stdio; - -pub(crate) use clocks::{strip_clocks_virt, stub_clocks_virt}; -pub(crate) use filesystem::{strip_fs_virt, stub_fs_virt}; -pub(crate) use http::{strip_http_virt, stub_http_virt}; -pub(crate) use io::{strip_io_virt, stub_io_virt}; -pub(crate) use sockets::{strip_sockets_virt, stub_sockets_virt}; -pub(crate) use stdio::{strip_stdio_virt, stub_stdio_virt}; - pub type VirtualFiles = BTreeMap; -/// How to deal with a stubbed/stripped export -/// -/// This enum is used mostly for nearly-static data, which exists -/// to make it easy to iterate over stubbing modules & functions that must -/// be manipulated. -enum StubRequirement { - /// The import/export that is stubbed/stripped must be present *and* must be replaced - Required, - /// The import/export that is stubbed/stripped is allowed to be missing - Optional, - /// Whether the import/export must be present and replaced - /// depends on external factors (use-case specific), in this case - /// whether the filesystem is used or not - DependsOnFsUsage, -} - #[derive(ValueEnum, Clone, Debug, Default, Deserialize)] #[serde(rename_all = "kebab-case")] pub enum StdioCfg { @@ -387,10 +357,10 @@ pub(crate) fn create_io_virt<'a>( StdioCfg::Deny => {} } } else { - strip_stdio_virt(module)?; + strip_virt(module, &["wasi:cli/std", "wasi:cli/terminal"])?; } if disable_stdio { - stub_stdio_virt(module)?; + stub_virt(module, &["wasi:cli/std", "wasi:cli/terminal"], false)?; } // First we iterate the options and fill in all HostDir and HostFile entries @@ -547,9 +517,9 @@ pub(crate) fn create_io_virt<'a>( // replacing it with a stub panic if !fs_passthrough { if disable_stdio { - stub_io_virt(module)?; + stub_virt(module, &["wasi:io/"], false)?; } - stub_fs_virt(module, true)?; + stub_virt(module, &["wasi:filesystem/"], false)?; } else { flags |= FLAGS_HOST_PASSTHROUGH; } diff --git a/src/virt_io/clocks.rs b/src/virt_io/clocks.rs deleted file mode 100644 index 3a6b0d6..0000000 --- a/src/virt_io/clocks.rs +++ /dev/null @@ -1,75 +0,0 @@ -use anyhow::{bail, Context, Result}; -use walrus::Module; - -use super::StubRequirement; - -/// Imports exposed by WASI for clocks functionality which are allowed to be -const WASI_CLOCKS_IMPORTS: [(&str, &str, &StubRequirement); 4] = [ - ( - "wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10", - "now", - &StubRequirement::Required, - ), - ( - "wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10", - "resolution", - &StubRequirement::Required, - ), - ( - "wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10", - "subscribe-instant", - &StubRequirement::Required, - ), - ( - "wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10", - "subscribe-duration", - &StubRequirement::Required, - ), -]; - -/// Replace imported WASI functions that implement clocks access with no-ops -pub(crate) fn stub_clocks_virt(module: &mut Module) -> Result<()> { - for (module_name, func_name, stub_requirement) in WASI_CLOCKS_IMPORTS { - match stub_requirement { - StubRequirement::Required => { - let fid = module - .imports - .get_func(module_name, func_name) - .with_context(|| { - format!( - "failed to find required clocks import [{func_name}] in module [{module_name}]" - ) - })?; - module - .replace_imported_func(fid, |(body, _)| { - body.unreachable(); - }) - .with_context(|| { - "failed to stub clocks functionality [{}] in module [{export_name}]" - })?; - } - _ => bail!("unexpected stub requirement in imports for WASI clocks"), - } - } - Ok(()) -} - -/// Exported functions related to WASI clocks -const WASI_CLOCK_EXPORTS: [&str; 4] = [ - "wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10#now", - "wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10#resolution", - "wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10#subscribe-instant", - "wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10#subscribe-duration", -]; - -/// Strip exported WASI functions that implement clock access -pub(crate) fn strip_clocks_virt(module: &mut Module) -> Result<()> { - stub_clocks_virt(module)?; - for export_name in WASI_CLOCK_EXPORTS { - module - .exports - .remove(export_name) - .with_context(|| format!("failed to strip WASI clocks function [{export_name}]"))?; - } - Ok(()) -} diff --git a/src/virt_io/filesystem.rs b/src/virt_io/filesystem.rs deleted file mode 100644 index 379edc3..0000000 --- a/src/virt_io/filesystem.rs +++ /dev/null @@ -1,255 +0,0 @@ -use anyhow::{Context, Result}; -use walrus::Module; - -use super::StubRequirement; - -/// Imports exposed by WASI for Filesystem functionality -/// -/// Some are allowed to be missing, some are required depending on -/// whether the FS is used or not (`fs_used` in `stub_fs_virt`) -const WASI_FS_IMPORTS: &[(&str, &str, &StubRequirement)] = &[ - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]descriptor.advise", - &StubRequirement::Optional, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]descriptor.append-via-stream", - &StubRequirement::Optional, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]descriptor.create-directory-at", - &StubRequirement::Optional, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]descriptor.get-flags", - &StubRequirement::Optional, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]descriptor.link-at", - &StubRequirement::Optional, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]descriptor.metadata-hash", - &StubRequirement::Optional, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]descriptor.metadata-hash-at", - &StubRequirement::Optional, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]descriptor.read-directory", - &StubRequirement::Optional, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]descriptor.readlink-at", - &StubRequirement::Optional, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]descriptor.remove-directory-at", - &StubRequirement::Optional, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]descriptor.rename-at", - &StubRequirement::Optional, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]descriptor.set-size", - &StubRequirement::Optional, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]descriptor.set-times", - &StubRequirement::Optional, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]descriptor.set-times-at", - &StubRequirement::Optional, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]descriptor.symlink-at", - &StubRequirement::Optional, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]descriptor.sync", - &StubRequirement::Optional, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]descriptor.sync-data", - &StubRequirement::Optional, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]descriptor.unlink-file-at", - &StubRequirement::Optional, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]descriptor.write", - &StubRequirement::Optional, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]descriptor.write-via-stream", - &StubRequirement::Optional, - ), - ( - "wasi:filesystem/preopens@0.2.0-rc-2023-11-10", - "get-directories", - &StubRequirement::DependsOnFsUsage, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[resource-drop]descriptor", - &StubRequirement::DependsOnFsUsage, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[resource-drop]directory-entry-stream", - &StubRequirement::DependsOnFsUsage, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]descriptor.get-type", - &StubRequirement::DependsOnFsUsage, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]descriptor.is-same-object", - &StubRequirement::DependsOnFsUsage, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]descriptor.open-at", - &StubRequirement::DependsOnFsUsage, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]descriptor.read", - &StubRequirement::Optional, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]directory-entry-stream.read-directory-entry", - &StubRequirement::DependsOnFsUsage, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]descriptor.read-via-stream", - &StubRequirement::DependsOnFsUsage, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]descriptor.stat", - &StubRequirement::DependsOnFsUsage, - ), - ( - "wasi:filesystem/types@0.2.0-rc-2023-11-10", - "[method]descriptor.stat-at", - &StubRequirement::DependsOnFsUsage, - ), -]; - -/// Replace imported WASI functions that implement filesystem access with no-ops -// Stubs must be _comprehensive_ in order to act as full deny over entire subsystem -// when stubbing functions that are not part of the virtual adapter exports, we therefore -// have to create this functions fresh. -// Ideally, we should generate these stubs automatically from WASI definitions. -pub(crate) fn stub_fs_virt(module: &mut Module, uses_fs: bool) -> Result<()> { - // Replace the filesystem functions that are allowed to be missing - for (module_name, func_name, stub_requirement) in WASI_FS_IMPORTS { - match (stub_requirement, uses_fs) { - // If the stub is always required, or depends on FS usage and uses_fs is set - // then we *must* find the function and replace it - (StubRequirement::Required, _) | (StubRequirement::DependsOnFsUsage, true) => { - let fid = module.imports.get_func(module_name, func_name) - .with_context(|| format!("failed to find required filesystem import [{func_name}] in module [{module_name}]"))?; - - module - .replace_imported_func(fid, |(body, _)| { - body.unreachable(); - }) - .with_context(|| { - "failed to stub filesystem functionality [{}] in module [{export_name}]" - })?; - } - // If the stub is optional, or required w/ FS usage and fs is not used, we can replace - // the functions optimistically, and not fail if they are missing - (StubRequirement::Optional, _) | (StubRequirement::DependsOnFsUsage, false) => { - if let Ok(fid) = module.imports.get_func(module_name, func_name) { - module - .replace_imported_func(fid, |(body, _)| { - body.unreachable(); - }) - .with_context(|| { - "failed to stub filesystem functionality [{}] in module [{export_name}]" - })?; - } - } - }; - } - Ok(()) -} - -const WASI_FILESYSTEM_EXPORTS: &[&str] = &[ - "wasi:filesystem/preopens@0.2.0-rc-2023-11-10#get-directories", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]descriptor.read-via-stream", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]descriptor.write-via-stream", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]descriptor.append-via-stream", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]descriptor.advise", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]descriptor.sync-data", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]descriptor.get-flags", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]descriptor.get-type", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]descriptor.set-size", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]descriptor.set-times", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]descriptor.read", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]descriptor.write", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]descriptor.read-directory", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]descriptor.sync", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]descriptor.create-directory-at", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]descriptor.stat", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]descriptor.stat-at", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]descriptor.set-times-at", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]descriptor.link-at", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]descriptor.open-at", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]descriptor.readlink-at", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]descriptor.remove-directory-at", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]descriptor.rename-at", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]descriptor.symlink-at", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]descriptor.unlink-file-at", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]descriptor.metadata-hash", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]descriptor.metadata-hash-at", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]descriptor.is-same-object", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[dtor]descriptor", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[method]directory-entry-stream.read-directory-entry", - "wasi:filesystem/types@0.2.0-rc-2023-11-10#[dtor]directory-entry-stream", -]; - -/// Strip exported WASI functions that implement filesystem access -pub(crate) fn strip_fs_virt(module: &mut Module) -> Result<()> { - stub_fs_virt(module, false)?; - - for &export_name in WASI_FILESYSTEM_EXPORTS { - module - .exports - .remove(export_name) - .with_context(|| format!("failed to strip WASI FS function [{export_name}]"))?; - } - - Ok(()) -} diff --git a/src/virt_io/http.rs b/src/virt_io/http.rs deleted file mode 100644 index cf63823..0000000 --- a/src/virt_io/http.rs +++ /dev/null @@ -1,229 +0,0 @@ -use anyhow::{bail, Context, Result}; -use walrus::Module; - -use super::StubRequirement; - -/// Exported functions related to WASI http -const WASI_HTTP_EXPORTS: &[&str] = &[ - "wasi:http/types@0.2.0-rc-2023-10-18#[dtor]fields", - "wasi:http/types@0.2.0-rc-2023-10-18#[constructor]fields", - "wasi:http/types@0.2.0-rc-2023-10-18#[method]fields.get", - "wasi:http/types@0.2.0-rc-2023-10-18#[method]fields.set", - "wasi:http/types@0.2.0-rc-2023-10-18#[method]fields.delete", - "wasi:http/types@0.2.0-rc-2023-10-18#[method]fields.append", - "wasi:http/types@0.2.0-rc-2023-10-18#[method]fields.entries", - "wasi:http/types@0.2.0-rc-2023-10-18#[method]fields.clone", - "wasi:http/types@0.2.0-rc-2023-10-18#[method]incoming-body.stream", - "wasi:http/types@0.2.0-rc-2023-10-18#[static]incoming-body.finish", - "wasi:http/types@0.2.0-rc-2023-10-18#[static]outgoing-body.finish", - "wasi:http/types@0.2.0-rc-2023-10-18#[dtor]incoming-request", - "wasi:http/types@0.2.0-rc-2023-10-18#[dtor]incoming-body", - "wasi:http/types@0.2.0-rc-2023-10-18#[dtor]outgoing-request", - "wasi:http/types@0.2.0-rc-2023-10-18#[dtor]outgoing-body", - "wasi:http/types@0.2.0-rc-2023-10-18#[method]incoming-request.method", - "wasi:http/types@0.2.0-rc-2023-10-18#[method]incoming-request.path-with-query", - "wasi:http/types@0.2.0-rc-2023-10-18#[method]incoming-request.scheme", - "wasi:http/types@0.2.0-rc-2023-10-18#[method]incoming-request.authority", - "wasi:http/types@0.2.0-rc-2023-10-18#[method]incoming-request.headers", - "wasi:http/types@0.2.0-rc-2023-10-18#[method]incoming-request.consume", - "wasi:http/types@0.2.0-rc-2023-10-18#[constructor]outgoing-request", - "wasi:http/types@0.2.0-rc-2023-10-18#[method]outgoing-request.write", - "wasi:http/types@0.2.0-rc-2023-10-18#[dtor]response-outparam", - "wasi:http/types@0.2.0-rc-2023-10-18#[static]response-outparam.set", - "wasi:http/types@0.2.0-rc-2023-10-18#[dtor]incoming-response", - "wasi:http/types@0.2.0-rc-2023-10-18#[dtor]outgoing-response", - "wasi:http/types@0.2.0-rc-2023-10-18#[method]incoming-response.status", - "wasi:http/types@0.2.0-rc-2023-10-18#[method]incoming-response.headers", - "wasi:http/types@0.2.0-rc-2023-10-18#[method]incoming-response.consume", - "wasi:http/types@0.2.0-rc-2023-10-18#[constructor]outgoing-response", - "wasi:http/types@0.2.0-rc-2023-10-18#[method]outgoing-response.write", - "wasi:http/types@0.2.0-rc-2023-10-18#[method]outgoing-body.write", - "wasi:http/types@0.2.0-rc-2023-10-18#[dtor]future-trailers", - "wasi:http/types@0.2.0-rc-2023-10-18#[method]future-trailers.get", - "wasi:http/types@0.2.0-rc-2023-10-18#[method]future-trailers.subscribe", - "wasi:http/types@0.2.0-rc-2023-10-18#[dtor]future-incoming-response", - "wasi:http/types@0.2.0-rc-2023-10-18#[method]future-incoming-response.get", - "wasi:http/types@0.2.0-rc-2023-10-18#[method]future-incoming-response.subscribe", - "wasi:http/outgoing-handler@0.2.0-rc-2023-10-18#handle", -]; - -/// Strip exported WASI functions that implement HTTP access -pub(crate) fn strip_http_virt(module: &mut Module) -> Result<()> { - stub_http_virt(module)?; - for &export_name in WASI_HTTP_EXPORTS { - module - .exports - .remove(export_name) - .with_context(|| format!("failed to strip WASI HTTP function [{export_name}]"))?; - } - Ok(()) -} - -/// Imports exposed by WASI for HTTP functionality -const WASI_HTTP_IMPORTS: [(&str, &str, &StubRequirement); 32] = [ - ("wasi:http/types", "drop-fields", &StubRequirement::Optional), - ("wasi:http/types", "new-fields", &StubRequirement::Optional), - ("wasi:http/types", "fields-get", &StubRequirement::Optional), - ("wasi:http/types", "fields-set", &StubRequirement::Optional), - ( - "wasi:http/types", - "fields-delete", - &StubRequirement::Optional, - ), - ( - "wasi:http/types", - "fields-append", - &StubRequirement::Optional, - ), - ( - "wasi:http/types", - "fields-entries", - &StubRequirement::Optional, - ), - ( - "wasi:http/types", - "fields-clone", - &StubRequirement::Optional, - ), - ( - "wasi:http/types", - "finish-incoming-stream", - &StubRequirement::Optional, - ), - ( - "wasi:http/types", - "finish-outgoing-stream", - &StubRequirement::Optional, - ), - ( - "wasi:http/types", - "drop-incoming-request", - &StubRequirement::Optional, - ), - ( - "wasi:http/types", - "drop-outgoing-request", - &StubRequirement::Optional, - ), - ( - "wasi:http/types", - "incoming-request-method", - &StubRequirement::Optional, - ), - ( - "wasi:http/types", - "incoming-request-path-with-query", - &StubRequirement::Optional, - ), - ( - "wasi:http/types", - "incoming-request-scheme", - &StubRequirement::Optional, - ), - ( - "wasi:http/types", - "incoming-request-authority", - &StubRequirement::Optional, - ), - ( - "wasi:http/types", - "incoming-request-headers", - &StubRequirement::Optional, - ), - ( - "wasi:http/types", - "incoming-request-consume", - &StubRequirement::Optional, - ), - ( - "wasi:http/types", - "new-outgoing-request", - &StubRequirement::Optional, - ), - ( - "wasi:http/types", - "outgoing-request-write", - &StubRequirement::Optional, - ), - ( - "wasi:http/types", - "drop-response-outparam", - &StubRequirement::Optional, - ), - ( - "wasi:http/types", - "set-response-outparam", - &StubRequirement::Optional, - ), - ( - "wasi:http/types", - "drop-incoming-response", - &StubRequirement::Optional, - ), - ( - "wasi:http/types", - "drop-outgoing-response", - &StubRequirement::Optional, - ), - ( - "wasi:http/types", - "incoming-response-status", - &StubRequirement::Optional, - ), - ( - "wasi:http/types", - "incoming-response-headers", - &StubRequirement::Optional, - ), - ( - "wasi:http/types", - "incoming-response-consume", - &StubRequirement::Optional, - ), - ( - "wasi:http/types", - "new-outgoing-response", - &StubRequirement::Optional, - ), - ( - "wasi:http/types", - "outgoing-response-write", - &StubRequirement::Optional, - ), - ( - "wasi:http/types", - "drop-future-incoming-response", - &StubRequirement::Optional, - ), - ( - "wasi:http/types", - "future-incoming-response-get", - &StubRequirement::Optional, - ), - ( - "wasi:http/types", - "listen-to-future-incoming-response", - &StubRequirement::Optional, - ), -]; - -/// Replace imported WASI functions that implement HTTP access with no-ops -pub(crate) fn stub_http_virt(module: &mut Module) -> Result<()> { - for (module_name, func_name, stub_requirement) in WASI_HTTP_IMPORTS { - match stub_requirement { - StubRequirement::Optional => { - if let Ok(fid) = module.imports.get_func(module_name, func_name) { - module - .replace_imported_func(fid, |(body, _)| { - body.unreachable(); - }) - .with_context(|| { - "failed to stub WASI HTTP functionality [{}] in module [{export_name}]" - })?; - } - } - _ => bail!("unexpected stub requirement in imports for WASI HTTP"), - } - } - Ok(()) -} diff --git a/src/virt_io/io.rs b/src/virt_io/io.rs deleted file mode 100644 index 44ccf9f..0000000 --- a/src/virt_io/io.rs +++ /dev/null @@ -1,195 +0,0 @@ -use anyhow::{bail, Context, Result}; -use walrus::Module; - -use super::StubRequirement; - -/// Imports exposed by WASI for IO functionality -/// -/// Some imports are required, and others are optional. -const WASI_IO_IMPORTS: &[(&str, &str, &StubRequirement)] = &[ - ( - "wasi:io/streams@0.2.0-rc-2023-11-10", - "[method]output-stream.blocking-flush", - &StubRequirement::Optional, - ), - ( - "wasi:io/streams@0.2.0-rc-2023-11-10", - "[method]input-stream.blocking-read", - &StubRequirement::Required, - ), - ( - "wasi:io/streams@0.2.0-rc-2023-11-10", - "[method]input-stream.blocking-skip", - &StubRequirement::Required, - ), - ( - "wasi:io/streams@0.2.0-rc-2023-11-10", - "[method]output-stream.blocking-splice", - &StubRequirement::Required, - ), - ( - "wasi:io/streams@0.2.0-rc-2023-11-10", - "[method]output-stream.blocking-write-and-flush", - &StubRequirement::Optional, - ), - ( - "wasi:io/streams@0.2.0-rc-2023-11-10", - "[method]output-stream.check-write", - &StubRequirement::Optional, - ), - ( - "wasi:io/streams@0.2.0-rc-2023-11-10", - "[resource-drop]input-stream", - &StubRequirement::Required, - ), - ( - "wasi:io/streams@0.2.0-rc-2023-11-10", - "[resource-drop]output-stream", - &StubRequirement::Required, - ), - ( - "wasi:io/streams@0.2.0-rc-2023-11-10", - "[method]output-stream.flush", - &StubRequirement::Optional, - ), - ( - "wasi:io/streams@0.2.0-rc-2023-11-10", - "[method]input-stream.read", - &StubRequirement::Optional, - ), - ( - "wasi:io/streams@0.2.0-rc-2023-11-10", - "[method]input-stream.skip", - &StubRequirement::Required, - ), - ( - "wasi:io/streams@0.2.0-rc-2023-11-10", - "[method]output-stream.splice", - &StubRequirement::Required, - ), - ( - "wasi:io/streams@0.2.0-rc-2023-11-10", - "[method]input-stream.subscribe", - &StubRequirement::Required, - ), - ( - "wasi:io/streams@0.2.0-rc-2023-11-10", - "[method]output-stream.subscribe", - &StubRequirement::Required, - ), - ( - "wasi:io/streams@0.2.0-rc-2023-11-10", - "[method]output-stream.write", - &StubRequirement::Required, - ), - ( - "wasi:io/streams@0.2.0-rc-2023-11-10", - "[method]output-stream.write-zeroes", - &StubRequirement::Required, - ), - ( - "wasi:io/streams@0.2.0-rc-2023-11-10", - "[method]output-stream.blocking-write-zeroes-and-flush", - &StubRequirement::Required, - ), - ( - "wasi:io/error@0.2.0-rc-2023-11-10", - "[resource-drop]error", - &StubRequirement::Required, - ), - ( - "wasi:io/poll@0.2.0-rc-2023-11-10", - "[method]pollable.ready", - &StubRequirement::Required, - ), - ( - "wasi:io/poll@0.2.0-rc-2023-11-10", - "[method]pollable.block", - &StubRequirement::Required, - ), - ( - "wasi:io/poll@0.2.0-rc-2023-11-10", - "[resource-drop]pollable", - &StubRequirement::Required, - ), - ( - "wasi:io/poll@0.2.0-rc-2023-11-10", - "poll", - &StubRequirement::Required, - ), -]; - -/// Replace imported WASI functions that implement general I/O access with no-ops -pub(crate) fn stub_io_virt(module: &mut Module) -> Result<()> { - // Replace the I/O functions that are allowed to be missing - for (module_name, func_name, stub_requirement) in WASI_IO_IMPORTS { - match stub_requirement { - // If the stub is always required we *must* find the function and replace it - StubRequirement::Required => { - let fid = module.imports.get_func(module_name, func_name) - .with_context(|| format!("failed to find required io import [{func_name}] in module [{module_name}]"))?; - - module - .replace_imported_func(fid, |(body, _)| { - body.unreachable(); - }) - .with_context(|| { - "failed to stub filesystem functionality [{}] in module [{export_name}]" - })?; - } - // If the stub is optional, we can replace the functions optimistically, and not fail if they are missing - StubRequirement::Optional => { - if let Ok(fid) = module.imports.get_func(module_name, func_name) { - module - .replace_imported_func(fid, |(body, _)| { - body.unreachable(); - }) - .with_context(|| { - "failed to stub filesystem functionality [{}] in module [{export_name}]" - })?; - } - } - _ => bail!("unexpected stub requirement in imports for WASI I/O"), - }; - } - - Ok(()) -} - -/// Exported functions related to IO -const WASI_IO_EXPORTS: &[&str] = &[ - "wasi:io/streams@0.2.0-rc-2023-11-10#[method]output-stream.blocking-flush", - "wasi:io/streams@0.2.0-rc-2023-11-10#[method]input-stream.blocking-read", - "wasi:io/streams@0.2.0-rc-2023-11-10#[method]input-stream.blocking-skip", - "wasi:io/streams@0.2.0-rc-2023-11-10#[method]output-stream.blocking-splice", - "wasi:io/streams@0.2.0-rc-2023-11-10#[method]output-stream.blocking-write-and-flush", - "wasi:io/streams@0.2.0-rc-2023-11-10#[method]output-stream.check-write", - "wasi:io/streams@0.2.0-rc-2023-11-10#[dtor]input-stream", - "wasi:io/streams@0.2.0-rc-2023-11-10#[dtor]output-stream", - "wasi:io/error@0.2.0-rc-2023-11-10#[dtor]error", - "wasi:io/error@0.2.0-rc-2023-11-10#[method]error.to-debug-string", - "wasi:io/streams@0.2.0-rc-2023-11-10#[method]output-stream.flush", - "wasi:io/streams@0.2.0-rc-2023-11-10#[method]input-stream.read", - "wasi:io/streams@0.2.0-rc-2023-11-10#[method]input-stream.skip", - "wasi:io/streams@0.2.0-rc-2023-11-10#[method]output-stream.splice", - "wasi:io/streams@0.2.0-rc-2023-11-10#[method]input-stream.subscribe", - "wasi:io/streams@0.2.0-rc-2023-11-10#[method]output-stream.subscribe", - "wasi:io/streams@0.2.0-rc-2023-11-10#[method]output-stream.write", - "wasi:io/streams@0.2.0-rc-2023-11-10#[method]output-stream.write-zeroes", - "wasi:io/streams@0.2.0-rc-2023-11-10#[method]output-stream.blocking-write-zeroes-and-flush", - "wasi:io/poll@0.2.0-rc-2023-11-10#[method]pollable.ready", - "wasi:io/poll@0.2.0-rc-2023-11-10#[method]pollable.block", - "wasi:io/poll@0.2.0-rc-2023-11-10#[dtor]pollable", - "wasi:io/poll@0.2.0-rc-2023-11-10#poll", -]; - -/// Strip exported WASI functions that implement IO (streams, polling) access -pub(crate) fn strip_io_virt(module: &mut Module) -> Result<()> { - stub_io_virt(module)?; - for &export_name in WASI_IO_EXPORTS { - module.exports.remove(export_name).with_context(|| { - format!("failed to strip general I/O export function [{export_name}]") - })?; - } - Ok(()) -} diff --git a/src/virt_io/sockets.rs b/src/virt_io/sockets.rs deleted file mode 100644 index 55c5372..0000000 --- a/src/virt_io/sockets.rs +++ /dev/null @@ -1,345 +0,0 @@ -use anyhow::{bail, Context, Result}; -use walrus::Module; - -use super::StubRequirement; - -/// Imports exposed by WASI for sockets functionality which are allowed to be missing -const WASI_SOCKETS_IMPORTS: [(&str, &str, &StubRequirement); 49] = [ - ( - "wasi:sockets/ip-name-lookup@0.2.0-rc-2023-10-18", - "resolve-addresses", - &StubRequirement::Required, - ), - ( - "wasi:sockets/ip-name-lookup@0.2.0-rc-2023-10-18", - "[method]resolve-address-stream.resolve-next-address", - &StubRequirement::Required, - ), - ( - "wasi:sockets/ip-name-lookup@0.2.0-rc-2023-10-18", - "[resource-drop]resolve-address-stream", - &StubRequirement::Required, - ), - ( - "wasi:sockets/ip-name-lookup@0.2.0-rc-2023-10-18", - "[method]resolve-address-stream.subscribe", - &StubRequirement::Required, - ), - ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18", - "[method]tcp-socket.start-bind", - &StubRequirement::Required, - ), - ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18", - "[method]tcp-socket.finish-bind", - &StubRequirement::Required, - ), - ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18", - "[method]tcp-socket.start-connect", - &StubRequirement::Required, - ), - ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18", - "[method]tcp-socket.finish-connect", - &StubRequirement::Required, - ), - ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18", - "[method]tcp-socket.start-listen", - &StubRequirement::Required, - ), - ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18", - "[method]tcp-socket.finish-listen", - &StubRequirement::Required, - ), - ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18", - "[method]tcp-socket.accept", - &StubRequirement::Required, - ), - ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18", - "[method]tcp-socket.local-address", - &StubRequirement::Required, - ), - ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18", - "[method]tcp-socket.remote-address", - &StubRequirement::Required, - ), - ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18", - "[method]tcp-socket.address-family", - &StubRequirement::Required, - ), - ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18", - "[method]tcp-socket.ipv6-only", - &StubRequirement::Required, - ), - ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18", - "[method]tcp-socket.set-ipv6-only", - &StubRequirement::Required, - ), - ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18", - "[method]tcp-socket.set-listen-backlog-size", - &StubRequirement::Required, - ), - ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18", - "[method]tcp-socket.keep-alive", - &StubRequirement::Required, - ), - ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18", - "[method]tcp-socket.set-keep-alive", - &StubRequirement::Required, - ), - ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18", - "[method]tcp-socket.no-delay", - &StubRequirement::Required, - ), - ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18", - "[method]tcp-socket.set-no-delay", - &StubRequirement::Required, - ), - ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18", - "[method]tcp-socket.unicast-hop-limit", - &StubRequirement::Required, - ), - ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18", - "[method]tcp-socket.set-unicast-hop-limit", - &StubRequirement::Required, - ), - ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18", - "[method]tcp-socket.receive-buffer-size", - &StubRequirement::Required, - ), - ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18", - "[method]tcp-socket.set-receive-buffer-size", - &StubRequirement::Required, - ), - ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18", - "[method]tcp-socket.send-buffer-size", - &StubRequirement::Required, - ), - ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18", - "[method]tcp-socket.set-send-buffer-size", - &StubRequirement::Required, - ), - ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18", - "[method]tcp-socket.subscribe", - &StubRequirement::Required, - ), - ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18", - "[method]tcp-socket.shutdown", - &StubRequirement::Required, - ), - ( - "wasi:sockets/tcp@0.2.0-rc-2023-10-18", - "[resource-drop]tcp-socket", - &StubRequirement::Required, - ), - ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18", - "[method]udp-socket.start-bind", - &StubRequirement::Required, - ), - ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18", - "[method]udp-socket.finish-bind", - &StubRequirement::Required, - ), - ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18", - "[method]udp-socket.start-connect", - &StubRequirement::Required, - ), - ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18", - "[method]udp-socket.finish-connect", - &StubRequirement::Required, - ), - ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18", - "[method]udp-socket.receive", - &StubRequirement::Required, - ), - ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18", - "[method]udp-socket.send", - &StubRequirement::Required, - ), - ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18", - "[method]udp-socket.local-address", - &StubRequirement::Required, - ), - ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18", - "[method]udp-socket.remote-address", - &StubRequirement::Required, - ), - ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18", - "[method]udp-socket.address-family", - &StubRequirement::Required, - ), - ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18", - "[method]udp-socket.ipv6-only", - &StubRequirement::Required, - ), - ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18", - "[method]udp-socket.set-ipv6-only", - &StubRequirement::Required, - ), - ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18", - "[method]udp-socket.unicast-hop-limit", - &StubRequirement::Required, - ), - ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18", - "[method]udp-socket.set-unicast-hop-limit", - &StubRequirement::Required, - ), - ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18", - "[method]udp-socket.receive-buffer-size", - &StubRequirement::Required, - ), - ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18", - "[method]udp-socket.set-receive-buffer-size", - &StubRequirement::Required, - ), - ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18", - "[method]udp-socket.send-buffer-size", - &StubRequirement::Required, - ), - ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18", - "[method]udp-socket.set-send-buffer-size", - &StubRequirement::Required, - ), - ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18", - "[method]udp-socket.subscribe", - &StubRequirement::Required, - ), - ( - "wasi:sockets/udp@0.2.0-rc-2023-10-18", - "[resource-drop]udp-socket", - &StubRequirement::Required, - ), -]; - -/// Replace imported WASI functions that implement socket access with no-ops -pub(crate) fn stub_sockets_virt(module: &mut Module) -> Result<()> { - for (module_name, func_name, stub_requirement) in WASI_SOCKETS_IMPORTS { - match stub_requirement { - StubRequirement::Required => { - let fid = module - .imports - .get_func(module_name, func_name) - .with_context(|| { - format!( - "failed to find required sockets import [{func_name}] in module [{module_name}]" - ) - })?; - module - .replace_imported_func(fid, |(body, _)| { - body.unreachable(); - }) - .with_context(|| { - "failed to stub sockets functionality [{}] in module [{export_name}]" - })?; - } - _ => bail!("unexpected stub requirement in imports for WASI sockets"), - } - } - - Ok(()) -} - -/// Exported functions related to sockets -const WASI_SOCKETS_EXPORTS: [&str; 49] = [ - "wasi:sockets/ip-name-lookup@0.2.0-rc-2023-10-18#resolve-addresses", - "wasi:sockets/ip-name-lookup@0.2.0-rc-2023-10-18#[method]resolve-address-stream.resolve-next-address", - "wasi:sockets/ip-name-lookup@0.2.0-rc-2023-10-18#[dtor]resolve-address-stream", - "wasi:sockets/ip-name-lookup@0.2.0-rc-2023-10-18#[method]resolve-address-stream.subscribe", - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#[method]tcp-socket.start-bind", - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#[method]tcp-socket.finish-bind", - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#[method]tcp-socket.start-connect", - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#[method]tcp-socket.finish-connect", - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#[method]tcp-socket.start-listen", - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#[method]tcp-socket.finish-listen", - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#[method]tcp-socket.accept", - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#[method]tcp-socket.local-address", - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#[method]tcp-socket.remote-address", - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#[method]tcp-socket.address-family", - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#[method]tcp-socket.ipv6-only", - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#[method]tcp-socket.set-ipv6-only", - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#[method]tcp-socket.set-listen-backlog-size", - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#[method]tcp-socket.keep-alive", - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#[method]tcp-socket.set-keep-alive", - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#[method]tcp-socket.no-delay", - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#[method]tcp-socket.set-no-delay", - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#[method]tcp-socket.unicast-hop-limit", - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#[method]tcp-socket.set-unicast-hop-limit", - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#[method]tcp-socket.receive-buffer-size", - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#[method]tcp-socket.set-receive-buffer-size", - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#[method]tcp-socket.send-buffer-size", - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#[method]tcp-socket.set-send-buffer-size", - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#[method]tcp-socket.subscribe", - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#[method]tcp-socket.shutdown", - "wasi:sockets/tcp@0.2.0-rc-2023-10-18#[dtor]tcp-socket", - "wasi:sockets/udp@0.2.0-rc-2023-10-18#[method]udp-socket.start-bind", - "wasi:sockets/udp@0.2.0-rc-2023-10-18#[method]udp-socket.finish-bind", - "wasi:sockets/udp@0.2.0-rc-2023-10-18#[method]udp-socket.start-connect", - "wasi:sockets/udp@0.2.0-rc-2023-10-18#[method]udp-socket.finish-connect", - "wasi:sockets/udp@0.2.0-rc-2023-10-18#[method]udp-socket.receive", - "wasi:sockets/udp@0.2.0-rc-2023-10-18#[method]udp-socket.send", - "wasi:sockets/udp@0.2.0-rc-2023-10-18#[method]udp-socket.local-address", - "wasi:sockets/udp@0.2.0-rc-2023-10-18#[method]udp-socket.remote-address", - "wasi:sockets/udp@0.2.0-rc-2023-10-18#[method]udp-socket.address-family", - "wasi:sockets/udp@0.2.0-rc-2023-10-18#[method]udp-socket.ipv6-only", - "wasi:sockets/udp@0.2.0-rc-2023-10-18#[method]udp-socket.set-ipv6-only", - "wasi:sockets/udp@0.2.0-rc-2023-10-18#[method]udp-socket.unicast-hop-limit", - "wasi:sockets/udp@0.2.0-rc-2023-10-18#[method]udp-socket.set-unicast-hop-limit", - "wasi:sockets/udp@0.2.0-rc-2023-10-18#[method]udp-socket.receive-buffer-size", - "wasi:sockets/udp@0.2.0-rc-2023-10-18#[method]udp-socket.set-receive-buffer-size", - "wasi:sockets/udp@0.2.0-rc-2023-10-18#[method]udp-socket.send-buffer-size", - "wasi:sockets/udp@0.2.0-rc-2023-10-18#[method]udp-socket.set-send-buffer-size", - "wasi:sockets/udp@0.2.0-rc-2023-10-18#[method]udp-socket.subscribe", - "wasi:sockets/udp@0.2.0-rc-2023-10-18#[dtor]udp-socket", -]; - -/// Strip exported WASI functions that implement sockets access -pub(crate) fn strip_sockets_virt(module: &mut Module) -> Result<()> { - stub_sockets_virt(module)?; - for export_name in WASI_SOCKETS_EXPORTS { - module.exports.remove(export_name).with_context(|| { - format!("failed to strip WASI sockets export function [{export_name}]") - })?; - } - Ok(()) -} diff --git a/src/virt_io/stdio.rs b/src/virt_io/stdio.rs deleted file mode 100644 index 4a9bfc7..0000000 --- a/src/virt_io/stdio.rs +++ /dev/null @@ -1,95 +0,0 @@ -use anyhow::{bail, Context, Result}; -use walrus::Module; - -use super::StubRequirement; - -/// Imports exposed by WASI for STDIO functionality which are allowed to be missing -const WASI_STDIO_IMPORTS: &[(&str, &str, &StubRequirement)] = &[ - ( - "wasi:cli/stdin@0.2.0-rc-2023-12-05", - "get-stdin", - &StubRequirement::Optional, - ), - ( - "wasi:cli/stdout@0.2.0-rc-2023-12-05", - "get-stdout", - &StubRequirement::Optional, - ), - ( - "wasi:cli/stderr@0.2.0-rc-2023-12-05", - "get-stderr", - &StubRequirement::Optional, - ), - ( - "wasi:cli/terminal-stdin@0.2.0-rc-2023-12-05", - "get-terminal-stdin", - &StubRequirement::Optional, - ), - ( - "wasi:cli/terminal-stdout@0.2.0-rc-2023-12-05", - "get-terminal-stdout", - &StubRequirement::Optional, - ), - ( - "wasi:cli/terminal-stderr@0.2.0-rc-2023-12-05", - "get-terminal-stderr", - &StubRequirement::Optional, - ), - ( - "wasi:cli/terminal-input@0.2.0-rc-2023-12-05", - "drop-terminal-input", - &StubRequirement::Optional, - ), - ( - "wasi:cli/terminal-output@0.2.0-rc-2023-12-05", - "drop-terminal-output", - &StubRequirement::Optional, - ), -]; - -/// Replace imported WASI functions that implement STDIO access with no-ops -pub(crate) fn stub_stdio_virt(module: &mut Module) -> Result<()> { - for (module_name, func_name, stub_requirement) in WASI_STDIO_IMPORTS { - match stub_requirement { - StubRequirement::Optional => { - if let Ok(fid) = module.imports.get_func(module_name, func_name) { - module - .replace_imported_func(fid, |(body, _)| { - body.unreachable(); - }) - .with_context(|| { - format!( - "failed to stub STDIO functionality [{func_name}] in module [{module_name}]" - ) - })?; - } - } - _ => bail!("unexpected stub requirement in imports for WASI STD I/O"), - } - } - Ok(()) -} - -/// Exported functions related to STDIO -const WASI_STDIO_EXPORTS: [&str; 8] = [ - "wasi:cli/stdin@0.2.0-rc-2023-12-05#get-stdin", - "wasi:cli/stdout@0.2.0-rc-2023-12-05#get-stdout", - "wasi:cli/stderr@0.2.0-rc-2023-12-05#get-stderr", - "wasi:cli/terminal-stdin@0.2.0-rc-2023-12-05#get-terminal-stdin", - "wasi:cli/terminal-stdout@0.2.0-rc-2023-12-05#get-terminal-stdout", - "wasi:cli/terminal-stderr@0.2.0-rc-2023-12-05#get-terminal-stderr", - "wasi:cli/terminal-input@0.2.0-rc-2023-12-05#[dtor]terminal-input", - "wasi:cli/terminal-output@0.2.0-rc-2023-12-05#[dtor]terminal-output", -]; - -/// Strip exported WASI functions that implement standard I/O (stdin, stdout, etc) access -pub(crate) fn strip_stdio_virt(module: &mut Module) -> Result<()> { - stub_stdio_virt(module)?; - for export_name in WASI_STDIO_EXPORTS { - module - .exports - .remove(export_name) - .with_context(|| format!("failed to strip std I/O function [{export_name}]"))?; - } - Ok(()) -} diff --git a/src/walrus_ops.rs b/src/walrus_ops.rs index c9386d6..7ff3a17 100644 --- a/src/walrus_ops.rs +++ b/src/walrus_ops.rs @@ -86,3 +86,64 @@ pub(crate) fn bump_stack_global(module: &mut Module, offset: i32) -> Result *stack_value = new_stack_value; Ok(new_stack_value as u32) } + +pub(crate) fn strip_virt(module: &mut Module, subsystems: &[&str]) -> Result<()> { + stub_virt(module, subsystems, true)?; + let mut subsystem_exports = Vec::new(); + for export in module.exports.iter() { + let export_name = if export.name.starts_with("cabi_post_") { + &export.name[10..] + } else { + &export.name + }; + if subsystems + .iter() + .any(|subsystem| export_name.starts_with(subsystem)) + { + subsystem_exports.push(export.name.to_string()); + } + } + for export_name in subsystem_exports { + module + .exports + .remove(&export_name) + .with_context(|| format!("failed to strip function [{export_name}]"))?; + } + Ok(()) +} + +/// Replace imported WASI functions that implement subsystem access with no-ops +pub(crate) fn stub_virt( + module: &mut Module, + subsystems: &[&str], + with_exports: bool, +) -> Result<()> { + let mut subsystem_imports = Vec::new(); + for import in module.imports.iter() { + let module_name = if with_exports && import.module.starts_with("[export]") { + &import.module[8..] + } else { + &import.module + }; + if subsystems + .iter() + .any(|subsystem| module_name.starts_with(subsystem)) + { + subsystem_imports.push((import.module.to_string(), import.name.to_string())); + } + } + for (module_name, func_name) in &subsystem_imports { + if let Ok(fid) = module.imports.get_func(module_name, func_name) { + module + .replace_imported_func(fid, |(body, _)| { + body.unreachable(); + }) + .with_context(|| { + format!( + "failed to stub WASI functionality [{func_name}] in module [{module_name}]" + ) + })?; + } + } + Ok(()) +} diff --git a/tests/cases/encapsulate.toml b/tests/cases/encapsulate.toml index fbfd176..f09d554 100644 --- a/tests/cases/encapsulate.toml +++ b/tests/cases/encapsulate.toml @@ -6,7 +6,7 @@ host-fs-path = "/LICENSE" exit = false fs.host-preopens = false stdio.stdin = "ignore" -stdio.stdout = "ignore" +stdio.stdout = "allow" stdio.stderr = "ignore" clocks = true http = false diff --git a/tests/cases/fs-nested-dir-read.toml b/tests/cases/fs-nested-dir-read.toml index a52ce39..faca022 100644 --- a/tests/cases/fs-nested-dir-read.toml +++ b/tests/cases/fs-nested-dir-read.toml @@ -11,7 +11,7 @@ stderr = "allow" virtualize = "./wit/deps" [expect] -file-read = '''package wasi:clocks@0.2.0-rc-2023-11-10; +file-read = '''package wasi:clocks@0.2.0; /// WASI Monotonic Clock is a clock API intended to let users measure elapsed /// time. /// @@ -23,7 +23,7 @@ file-read = '''package wasi:clocks@0.2.0-rc-2023-11-10; /// /// It is intended for measuring elapsed time. interface monotonic-clock { - use wasi:io/poll@0.2.0-rc-2023-11-10.{pollable}; + use wasi:io/poll@0.2.0.{pollable}; /// An instant in time, in nanoseconds. An instant is relative to an /// unspecified initial value, and can only be compared to instances from diff --git a/tests/virt.rs b/tests/virt.rs index 5823ce7..61bbb51 100644 --- a/tests/virt.rs +++ b/tests/virt.rs @@ -8,12 +8,13 @@ use std::{fs, path::PathBuf}; use wasi_virt::WasiVirt; use wasm_compose::composer::ComponentComposer; use wasmparser::{Chunk, Parser, Payload}; +use wasmtime::component::ResourceTable; use wasmtime::{ component::{Component, Linker}, Config, Engine, Store, WasmBacktraceDetails, }; use wasmtime_wasi::preview2::command::add_to_linker; -use wasmtime_wasi::preview2::{DirPerms, FilePerms, Table, WasiCtx, WasiCtxBuilder, WasiView}; +use wasmtime_wasi::preview2::{DirPerms, FilePerms, WasiCtx, WasiCtxBuilder, WasiView}; use wasmtime_wasi::Dir; use wit_component::ComponentEncoder; @@ -76,7 +77,7 @@ async fn virt_test() -> Result<()> { let test_case_name = test_case_file_name.strip_suffix(".toml").unwrap(); // Filtering... - // if test_case_name != "stdio" { + // if !test_case_name.starts_with("fs") { // continue; // } @@ -108,11 +109,12 @@ async fn virt_test() -> Result<()> { } // encode the component - let component_core = fs::read(&format!( + let component_core_path = &format!( "target/wasm32-wasi/{}/{}.wasm", if DEBUG { "debug" } else { "release" }, component_name.to_snake_case() - ))?; + ); + let component_core = fs::read(component_core_path)?; let mut encoder = ComponentEncoder::default() .validate(true) .module(&component_core)?; @@ -138,9 +140,12 @@ async fn virt_test() -> Result<()> { } } - let virt_component = virt_opts - .finish() - .with_context(|| format!("Error creating virtual adapter for {:?}", test_case_path))?; + let virt_component = virt_opts.finish().with_context(|| { + format!( + "Error creating virtual adapter {:?} for {:?}", + test_case_path, component_core_path + ) + })?; fs::write(&virt_component_path, &virt_component.adapter)?; @@ -189,7 +194,7 @@ async fn virt_test() -> Result<()> { builder.env(k, v); } } - let table = Table::new(); + let table = ResourceTable::new(); let wasi = builder.build(); let mut config = Config::new(); @@ -204,14 +209,14 @@ async fn virt_test() -> Result<()> { let component = Component::from_binary(&engine, &component_bytes).unwrap(); struct CommandCtx { - table: Table, + table: ResourceTable, wasi: WasiCtx, } impl WasiView for CommandCtx { - fn table(&self) -> &Table { + fn table(&self) -> &ResourceTable { &self.table } - fn table_mut(&mut self) -> &mut Table { + fn table_mut(&mut self) -> &mut ResourceTable { &mut self.table } fn ctx(&self) -> &WasiCtx { diff --git a/virtual-adapter/src/io.rs b/virtual-adapter/src/io.rs index 540b3cd..327b425 100644 --- a/virtual-adapter/src/io.rs +++ b/virtual-adapter/src/io.rs @@ -15,11 +15,13 @@ use crate::exports::wasi::filesystem::types::{ }; use crate::exports::wasi::http::outgoing_handler::Guest as OutgoingHandler; use crate::exports::wasi::http::types::{ - Error as HttpError, Fields, FutureIncomingResponse, FutureTrailers, GuestFields, - GuestFutureIncomingResponse, GuestFutureTrailers, GuestIncomingBody, GuestIncomingRequest, - GuestIncomingResponse, GuestOutgoingBody, GuestOutgoingRequest, GuestOutgoingResponse, - GuestResponseOutparam, IncomingBody, IncomingRequest, IncomingResponse, Method, OutgoingBody, - OutgoingRequest, OutgoingResponse, RequestOptions, ResponseOutparam, Scheme, StatusCode, + DnsErrorPayload, ErrorCode as HttpErrorCode, FieldSizePayload, Fields, FutureIncomingResponse, + FutureTrailers, Guest as GuestHttpTypes, GuestFields, GuestFutureIncomingResponse, + GuestFutureTrailers, GuestIncomingBody, GuestIncomingRequest, GuestIncomingResponse, + GuestOutgoingBody, GuestOutgoingRequest, GuestOutgoingResponse, GuestRequestOptions, + GuestResponseOutparam, HeaderError, Headers, IncomingBody, IncomingRequest, IncomingResponse, + Method, OutgoingBody, OutgoingRequest, OutgoingResponse, RequestOptions, ResponseOutparam, + Scheme, StatusCode, TlsAlertReceivedPayload, }; use crate::exports::wasi::io::error::GuestError as GuestStreamsError; use crate::exports::wasi::io::poll::{Guest as Poll, GuestPollable, Pollable}; @@ -28,17 +30,22 @@ use crate::exports::wasi::io::streams::{ StreamError, }; use crate::exports::wasi::sockets::ip_name_lookup::{ - Guest as IpNameLookup, GuestResolveAddressStream, IpAddress, IpAddressFamily, Network, - ResolveAddressStream, + Guest as IpNameLookup, GuestResolveAddressStream, IpAddress, Network, ResolveAddressStream, }; use crate::exports::wasi::sockets::tcp::{ - ErrorCode as NetworkErrorCode, GuestTcpSocket, IpSocketAddress, ShutdownType, TcpSocket, + Duration, ErrorCode as NetworkErrorCode, GuestTcpSocket, IpAddressFamily, IpSocketAddress, + ShutdownType, TcpSocket, +}; +use crate::exports::wasi::sockets::udp::{ + GuestIncomingDatagramStream, GuestOutgoingDatagramStream, GuestUdpSocket, IncomingDatagram, + OutgoingDatagram, UdpSocket, }; -use crate::exports::wasi::sockets::udp::{Datagram, GuestUdpSocket, UdpSocket}; -use crate::wasi::cli::stderr; use crate::wasi::cli::stdin; use crate::wasi::cli::stdout; +use crate::wasi::cli::terminal_input; +use crate::wasi::cli::terminal_output; +use crate::wasi::cli::{stderr, terminal_stderr, terminal_stdin, terminal_stdout}; use crate::wasi::filesystem::preopens; use crate::wasi::filesystem::types as filesystem_types; use crate::wasi::io::streams; @@ -105,7 +112,7 @@ fn log(msg: &str) { #[derive(Debug)] pub enum IoError { - Code(ErrorCode), + FsCode(ErrorCode), Host(streams::Error), } @@ -299,7 +306,7 @@ impl StaticIndexEntry { } if offset.get() as usize > unsafe { self.data.active.1 } { return Err(StreamError::LastOperationFailed(Resource::new( - StreamsError::Code(ErrorCode::InvalidSeek), + StreamsError::FsCode(ErrorCode::InvalidSeek), ))); } let read_ptr = unsafe { self.data.active.0.add(offset.get() as usize) }; @@ -317,7 +324,7 @@ impl StaticIndexEntry { } if offset.get() as usize > unsafe { self.data.passive.1 } { return Err(StreamError::LastOperationFailed(Resource::new( - StreamsError::Code(ErrorCode::InvalidSeek), + StreamsError::FsCode(ErrorCode::InvalidSeek), ))); } let read_len = cmp::min( @@ -337,7 +344,7 @@ impl StaticIndexEntry { } StaticIndexType::RuntimeDir | StaticIndexType::Dir => { Err(StreamError::LastOperationFailed(Resource::new( - StreamsError::Code(ErrorCode::IsDirectory), + StreamsError::FsCode(ErrorCode::IsDirectory), ))) } StaticIndexType::RuntimeFile => { @@ -406,8 +413,10 @@ pub enum FilesystemDirectoryEntryStream { Host(filesystem_types::DirectoryEntryStream), } -pub struct CliTerminalInput; -pub struct CliTerminalOutput; +pub struct CliTerminalInput(terminal_input::TerminalInput); +pub struct CliTerminalOutput(terminal_output::TerminalOutput); + +pub struct HttpTypes; pub struct HttpFields(http_types::Fields); pub struct HttpFutureIncomingResponse(http_types::FutureIncomingResponse); @@ -419,9 +428,13 @@ pub struct HttpOutgoingBody(http_types::OutgoingBody); pub struct HttpOutgoingRequest(http_types::OutgoingRequest); pub struct HttpOutgoingResponse(http_types::OutgoingResponse); pub struct HttpResponseOutparam(http_types::ResponseOutparam); + +pub struct HttpRequestOptions(http_types::RequestOptions); pub struct SocketsResolveAddressStream(ip_name_lookup::ResolveAddressStream); pub struct SocketsTcpSocket(tcp::TcpSocket); pub struct SocketsUdpSocket(udp::UdpSocket); +pub struct SocketsIncomingDatagramStream(udp::IncomingDatagramStream); +pub struct SocketsOutgoingDatagramStream(udp::OutgoingDatagramStream); pub struct IoState { initialized: bool, @@ -436,6 +449,12 @@ impl IoState { return; } + if DEBUG { + std::panic::set_hook(Box::new(|invoke| { + debug!("{:?}", invoke); + })); + } + if Io::host_passthrough() || Io::host_preopens() { let host_preopen_directories = unsafe { &mut STATE.host_preopen_directories }; for (fd, name) in preopens::get_directories() { @@ -546,21 +565,24 @@ impl Stderr for VirtAdapter { impl TerminalStdin for VirtAdapter { fn get_terminal_stdin() -> Option> { debug!("CALL wasi:cli/terminal-stdin#get-terminal-stdin"); - Some(Resource::new(TerminalInput)) + terminal_stdin::get_terminal_stdin() + .map(|terminal_input| Resource::new(CliTerminalInput(terminal_input))) } } impl TerminalStdout for VirtAdapter { fn get_terminal_stdout() -> Option> { debug!("CALL wasi:cli/terminal-stdout#get-terminal-stdout"); - Some(Resource::new(TerminalOutput)) + terminal_stdout::get_terminal_stdout() + .map(|terminal_output| Resource::new(CliTerminalOutput(terminal_output))) } } impl TerminalStderr for VirtAdapter { fn get_terminal_stderr() -> Option> { debug!("CALL wasi:cli/terminal-stderr#get-terminal-stderr"); - Some(Resource::new(TerminalOutput)) + terminal_stderr::get_terminal_stderr() + .map(|terminal_output| Resource::new(CliTerminalOutput(terminal_output))) } } @@ -587,7 +609,8 @@ impl MonotonicClock for VirtAdapter { impl FilesystemTypes for VirtAdapter { fn filesystem_error_code(err: &StreamsError) -> Option { - if let StreamsError::Code(code) = err { + debug!("CALL wasi:filesystem/types#filesystem-error-code"); + if let StreamsError::FsCode(code) = err { Some(*code) } else { None @@ -597,6 +620,7 @@ impl FilesystemTypes for VirtAdapter { impl Preopens for VirtAdapter { fn get_directories() -> Vec<(Resource, String)> { + debug!("CALL wasi:filesystem/preopens#get-directories"); IoState::initialize(); unsafe { &STATE.preopen_directories } .iter() @@ -608,17 +632,48 @@ impl Preopens for VirtAdapter { impl OutgoingHandler for VirtAdapter { fn handle( request: Resource, - options: Option, - ) -> Result, HttpError> { + options: Option>, + ) -> Result, HttpErrorCode> { outgoing_handler::handle( Resource::into_inner(request).0, - options.map(request_options_map), + options.map(|o| Resource::into_inner(o).0), ) .map(|response| Resource::new(HttpFutureIncomingResponse(response))) .map_err(http_err_map_rev) } } +impl GuestRequestOptions for RequestOptions { + fn new() -> Self { + debug!("CALL wasi:http/types#request-options.new"); + Self(http_types::RequestOptions::new()) + } + fn connect_timeout(&self) -> Option { + debug!("CALL wasi:http/types#request-options.connect-timeout"); + self.0.connect_timeout() + } + fn set_connect_timeout(&self, duration: Option) -> Result<(), ()> { + debug!("CALL wasi:http/types#request-options.set-connect-timeout"); + self.0.set_connect_timeout(duration) + } + fn first_byte_timeout(&self) -> Option { + debug!("CALL wasi:http/types#request-options.first-byte-timeout"); + self.0.first_byte_timeout() + } + fn set_first_byte_timeout(&self, duration: Option) -> Result<(), ()> { + debug!("CALL wasi:http/types#request-options.set-first-byte-timeout"); + self.0.set_first_byte_timeout(duration) + } + fn between_bytes_timeout(&self) -> Option { + debug!("CALL wasi:http/types#request-options.between-bytes-timeout"); + self.0.between_bytes_timeout() + } + fn set_between_bytes_timeout(&self, duration: Option) -> Result<(), ()> { + debug!("CALL wasi:http/types#request-options.set-between-bytes-timeout"); + self.0.set_between_bytes_timeout(duration) + } +} + impl GuestPollable for IoPollable { fn ready(&self) -> bool { debug!("CALL wasi:io/poll#pollable.ready PID={self:?}",); @@ -684,12 +739,10 @@ impl IpNameLookup for VirtAdapter { fn resolve_addresses( network: &Network, name: String, - address_family: Option, - include_unavailable: bool, ) -> Result, NetworkErrorCode> { debug!("CALL wasi:sockets/ip-name-lookup#resolve-addresses"); Ok(Resource::new(SocketsResolveAddressStream( - ip_name_lookup::resolve_addresses(network, &name, address_family, include_unavailable)?, + ip_name_lookup::resolve_addresses(network, &name)?, ))) } } @@ -1020,7 +1073,7 @@ impl GuestInputStream for InputStream { Self::Null => Ok(0), Self::Err => Err(StreamError::Closed), Self::StaticFile { .. } => Err(StreamError::LastOperationFailed(Resource::new( - StreamsError::Code(ErrorCode::Io), + StreamsError::FsCode(ErrorCode::Io), ))), Self::Host(descriptor) => descriptor.skip(offset).map_err(stream_err_map), } @@ -1031,7 +1084,7 @@ impl GuestInputStream for InputStream { Self::Null => Ok(0), Self::Err => Err(StreamError::Closed), Self::StaticFile { .. } => Err(StreamError::LastOperationFailed(Resource::new( - StreamsError::Code(ErrorCode::Io), + StreamsError::FsCode(ErrorCode::Io), ))), Self::Host(descriptor) => descriptor.blocking_skip(offset).map_err(stream_err_map), } @@ -1165,6 +1218,7 @@ impl GuestOutputStream for OutputStream { impl GuestStreamsError for StreamsError { fn to_debug_string(&self) -> String { + debug!("CALL wasi:io/error#to-debug-string"); format!("{self:?}") } } @@ -1182,30 +1236,40 @@ impl GuestDirectoryEntryStream for DirectoryEntryStream { } } +impl GuestHttpTypes for HttpTypes { + fn http_error_code(err: &IoError) -> Option { + debug!("CALL wasi:http/types#http-error-code"); + match err { + IoError::FsCode(_) => None, + IoError::Host(h) => http_types::http_error_code(h).map(|e| http_err_map_rev(e)), + } + } +} + impl GuestFields for Fields { - fn new(entries: Vec<(String, Vec)>) -> Self { + fn new() -> Self { debug!("CALL wasi:http/types#fields.constructor"); - Self(http_types::Fields::new(&entries)) + Self(http_types::Fields::new()) } fn get(&self, name: String) -> Vec> { - debug!("CALL wasi:http/types#fields.get"); + debug!("CALL wasi:http/types#fields.get NAME={name}"); self.0.get(&name) } - fn set(&self, name: String, values: Vec>) { - debug!("CALL wasi:http/types#fields.set"); - self.0.set(&name, &values) + fn set(&self, name: String, values: Vec>) -> Result<(), HeaderError> { + debug!("CALL wasi:http/types#fields.set NAME={name}"); + self.0.set(&name, &values).map_err(header_err_map_rev) } - fn delete(&self, name: String) { - debug!("CALL wasi:http/types#fields.delete"); - self.0.delete(&name) + fn delete(&self, name: String) -> Result<(), HeaderError> { + debug!("CALL wasi:http/types#fields.delete NAME={name}"); + self.0.delete(&name).map_err(header_err_map_rev) } - fn append(&self, name: String, value: Vec) { - debug!("CALL wasi:http/types#fields.append"); - self.0.append(&name, &value) + fn append(&self, name: String, value: Vec) -> Result<(), HeaderError> { + debug!("CALL wasi:http/types#fields.append NAME={name}"); + self.0.append(&name, &value).map_err(header_err_map_rev) } fn entries(&self) -> Vec<(String, Vec)> { @@ -1214,8 +1278,21 @@ impl GuestFields for Fields { } fn clone(&self) -> Resource { + debug!("CALL wasi:http/types#fields.clone"); Resource::new(Self(self.0.clone())) } + + fn from_list(list: Vec<(String, Vec)>) -> Result, HeaderError> { + debug!("CALL wasi:http/types#fields.from-list"); + http_types::Fields::from_list(&list) + .map(|fields| Resource::new(Self(fields))) + .map_err(header_err_map_rev) + } + + fn has(&self, name: String) -> bool { + debug!("CALL wasi:http/types#fields.has NAME={name}"); + self.0.has(&name) + } } impl GuestIncomingRequest for IncomingRequest { @@ -1246,31 +1323,57 @@ impl GuestIncomingRequest for IncomingRequest { } impl GuestOutgoingRequest for OutgoingRequest { - fn new( - method: Method, - path_with_query: Option, - scheme: Option, - authority: Option, - headers: &Fields, - ) -> Self { + fn new(headers: Resource) -> Self { debug!("CALL wasi:http/types#outgoing-request.new"); Self(http_types::OutgoingRequest::new( - &method_map(method), - path_with_query.as_deref(), - scheme.map(|s| scheme_map(s)).as_ref(), - authority.as_deref(), - &headers.0, + Resource::into_inner(headers).0, )) } - fn write(&self) -> Result, ()> { + fn body(&self) -> Result, ()> { debug!("CALL wasi:http/types#outgoing-request.write"); - Ok(Resource::new(HttpOutgoingBody(self.0.write()?))) + Ok(Resource::new(HttpOutgoingBody(self.0.body()?))) + } + + fn method(&self) -> Method { + method_map_rev(self.0.method()) + } + + fn set_method(&self, method: Method) -> Result<(), ()> { + self.0.set_method(&method_map(method)) + } + + fn path_with_query(&self) -> Option { + self.0.path_with_query() + } + + fn set_path_with_query(&self, path_with_query: Option) -> Result<(), ()> { + self.0.set_path_with_query(path_with_query.as_deref()) + } + + fn scheme(&self) -> Option { + self.0.scheme().map(scheme_map_rev) + } + + fn set_scheme(&self, scheme: Option) -> Result<(), ()> { + self.0.set_scheme(scheme.map(scheme_map).as_ref()) + } + + fn authority(&self) -> Option { + self.0.authority() + } + + fn set_authority(&self, authority: Option) -> Result<(), ()> { + self.0.set_authority(authority.as_deref()) + } + + fn headers(&self) -> Resource { + Resource::new(HttpFields(self.0.headers())) } } impl GuestResponseOutparam for ResponseOutparam { - fn set(param: Resource, response: Result, HttpError>) { + fn set(param: Resource, response: Result, HttpErrorCode>) { debug!("CALL wasi:http/types#response-outparam.set"); let param = Resource::into_inner(param).0; match response { @@ -1315,24 +1418,44 @@ impl GuestFutureTrailers for FutureTrailers { Resource::new(Pollable::Host(self.0.subscribe())) } - fn get(&self) -> Option, HttpError>> { + fn get(&self) -> Option>, HttpErrorCode>, ()>> { debug!("CALL wasi:http/types#future-trailers.get"); self.0.get().map(|r| { - r.map(|fields| Resource::new(HttpFields(fields))) - .map_err(|e| http_err_map_rev(e)) + r.map(|fields| { + fields + .map(|fields| fields.map(|fields| Resource::new(HttpFields(fields)))) + .map_err(http_err_map_rev) + }) }) } } impl GuestOutgoingResponse for OutgoingResponse { - fn new(status_code: StatusCode, headers: &Fields) -> Self { + fn new(headers: Resource) -> Self { debug!("CALL wasi:http/types#outgoing-response.constructor"); - Self(http_types::OutgoingResponse::new(status_code, &headers.0)) + Self(http_types::OutgoingResponse::new( + Resource::into_inner(headers).0, + )) + } + + fn status_code(&self) -> StatusCode { + debug!("CALL wasi:http/types#outgoing-response.status-code"); + self.0.status_code() + } + + fn set_status_code(&self, status_code: StatusCode) -> Result<(), ()> { + debug!("CALL wasi:http/types#outgoing-response.set-status-code"); + self.0.set_status_code(status_code) } - fn write(&self) -> Result, ()> { + fn headers(&self) -> Resource { + debug!("CALL wasi:http/types#outgoing-response.headers"); + Resource::new(HttpFields(self.0.headers())) + } + + fn body(&self) -> Result, ()> { debug!("CALL wasi:http/types#outgoing-response.body"); - Ok(Resource::new(HttpOutgoingBody(self.0.write()?))) + Ok(Resource::new(HttpOutgoingBody(self.0.body()?))) } } @@ -1349,12 +1472,16 @@ impl GuestOutgoingBody for OutgoingBody { Ok(Resource::new(OutputStream::Host(self.0.write()?))) } - fn finish(body: Resource, trailers: Option>) { + fn finish( + body: Resource, + trailers: Option>, + ) -> Result<(), HttpErrorCode> { debug!("CALL wasi:http/types#outgoing-body.finish"); http_types::OutgoingBody::finish( Resource::into_inner(body).0, trailers.map(|fields| Resource::into_inner(fields).0), ) + .map_err(http_err_map_rev) } } @@ -1364,7 +1491,7 @@ impl GuestFutureIncomingResponse for FutureIncomingResponse { Resource::new(Pollable::Host(self.0.subscribe())) } - fn get(&self) -> Option, HttpError>, ()>> { + fn get(&self) -> Option, HttpErrorCode>, ()>> { debug!("CALL wasi:http/types#future-incoming-response.get"); self.0.get().map(|r| { r.map(|r| { @@ -1453,45 +1580,57 @@ impl GuestTcpSocket for TcpSocket { debug!("CALL wasi:sockets/tcp#tcp-socket.remote-address"); self.0.remote_address() } + fn is_listening(&self) -> bool { + debug!("CALL wasi:sockets/tcp#tcp-socket.is-listening"); + self.0.is_listening() + } fn address_family(&self) -> IpAddressFamily { debug!("CALL wasi:sockets/tcp#tcp-socket.address-family"); self.0.address_family() } - fn ipv6_only(&self) -> Result { - debug!("CALL wasi:sockets/tcp#tcp-socket.ipv6-only"); - self.0.ipv6_only() - } - fn set_ipv6_only(&self, value: bool) -> Result<(), NetworkErrorCode> { - debug!("CALL wasi:sockets/tcp#tcp-socket.set-ipv6-only"); - self.0.set_ipv6_only(value) - } fn set_listen_backlog_size(&self, value: u64) -> Result<(), NetworkErrorCode> { debug!("CALL wasi:sockets/tcp#tcp-socket.set-listen-backlog-size"); self.0.set_listen_backlog_size(value) } - fn keep_alive(&self) -> Result { - debug!("CALL wasi:sockets/tcp#tcp-socket.keep-alive"); - self.0.keep_alive() + fn keep_alive_enabled(&self) -> Result { + debug!("CALL wasi:sockets/tcp#tcp-socket.keep-alive-enabled"); + self.0.keep_alive_enabled() } - fn set_keep_alive(&self, value: bool) -> Result<(), NetworkErrorCode> { - debug!("CALL wasi:sockets/tcp#tcp-socket.set-keep-alive"); - self.0.set_keep_alive(value) + fn set_keep_alive_enabled(&self, value: bool) -> Result<(), NetworkErrorCode> { + debug!("CALL wasi:sockets/tcp#tcp-socket.set-keep-alive-enabled"); + self.0.set_keep_alive_enabled(value) } - fn no_delay(&self) -> Result { - debug!("CALL wasi:sockets/tcp#tcp-socket.no-delay"); - self.0.no_delay() + fn keep_alive_idle_time(&self) -> Result { + debug!("CALL wasi:sockets/tcp#tcp-socket.keep-alive-idle-time"); + self.0.keep_alive_idle_time() } - fn set_no_delay(&self, value: bool) -> Result<(), NetworkErrorCode> { - debug!("CALL wasi:sockets/tcp#tcp-socket.set-no-delay"); - self.0.set_no_delay(value) + fn set_keep_alive_idle_time(&self, value: Duration) -> Result<(), NetworkErrorCode> { + debug!("CALL wasi:sockets/tcp#tcp-socket.set-keep-alive-idle-time"); + self.0.set_keep_alive_idle_time(value) } - fn unicast_hop_limit(&self) -> Result { - debug!("CALL wasi:sockets/tcp#tcp-socket.unicast-hop-limit"); - self.0.unicast_hop_limit() + fn keep_alive_interval(&self) -> Result { + debug!("CALL wasi:sockets/tcp#tcp-socket.keep-alive-interval-time"); + self.0.keep_alive_interval() } - fn set_unicast_hop_limit(&self, value: u8) -> Result<(), NetworkErrorCode> { - debug!("CALL wasi:sockets/tcp#tcp-socket.set-unicast-hop-limit"); - self.0.set_unicast_hop_limit(value) + fn set_keep_alive_interval(&self, value: Duration) -> Result<(), NetworkErrorCode> { + debug!("CALL wasi:sockets/tcp#tcp-socket.set-keep-alive-interval-time"); + self.0.set_keep_alive_interval(value) + } + fn keep_alive_count(&self) -> Result { + debug!("CALL wasi:sockets/tcp#tcp-socket.keep-alive-count-time"); + self.0.keep_alive_count() + } + fn set_keep_alive_count(&self, value: u32) -> Result<(), NetworkErrorCode> { + debug!("CALL wasi:sockets/tcp#tcp-socket.set-keep-alive-count-time"); + self.0.set_keep_alive_count(value) + } + fn hop_limit(&self) -> Result { + debug!("CALL wasi:sockets/tcp#tcp-socket.hop-limit"); + self.0.hop_limit() + } + fn set_hop_limit(&self, value: u8) -> Result<(), NetworkErrorCode> { + debug!("CALL wasi:sockets/tcp#tcp-socket.set-hop-limit"); + self.0.set_hop_limit(value) } fn receive_buffer_size(&self) -> Result { debug!("CALL wasi:sockets/tcp#tcp-socket.receive-buffer-size"); @@ -1536,44 +1675,6 @@ impl GuestUdpSocket for UdpSocket { debug!("CALL wasi:sockets/udp#udp-socket.finish-bind"); self.0.finish_bind() } - fn start_connect( - &self, - network: &Network, - remote_address: IpSocketAddress, - ) -> Result<(), NetworkErrorCode> { - debug!("CALL wasi:sockets/udp#udp-socket.start-connect"); - self.0.start_connect(network, remote_address) - } - fn finish_connect(&self) -> Result<(), NetworkErrorCode> { - debug!("CALL wasi:sockets/udp#udp-socket.finish-connect"); - self.0.finish_connect() - } - fn receive(&self, max_results: u64) -> Result, NetworkErrorCode> { - debug!("CALL wasi:sockets/udp#udp-socket.receive"); - match self.0.receive(max_results) { - Ok(mut datagrams) => Ok(datagrams - .drain(..) - .map(|d| Datagram { - data: d.data, - remote_address: d.remote_address, - }) - .collect::>()), - Err(err) => Err(err), - } - } - fn send(&self, mut datagrams: Vec) -> Result { - debug!("CALL wasi:sockets/udp#udp-socket.send"); - self.0.send( - datagrams - .drain(..) - .map(|d| udp::Datagram { - data: d.data, - remote_address: d.remote_address, - }) - .collect::>() - .as_slice(), - ) - } fn local_address(&self) -> Result { debug!("CALL wasi:sockets/udp#udp-socket.local-address"); self.0.local_address() @@ -1586,14 +1687,6 @@ impl GuestUdpSocket for UdpSocket { debug!("CALL wasi:sockets/udp#udp-socket.address-family"); self.0.address_family() } - fn ipv6_only(&self) -> Result { - debug!("CALL wasi:sockets/udp#udp-socket.ipv6-only"); - self.0.ipv6_only() - } - fn set_ipv6_only(&self, value: bool) -> Result<(), NetworkErrorCode> { - debug!("CALL wasi:sockets/udp#udp-socket.set-ipv6-only"); - self.0.set_ipv6_only(value) - } fn unicast_hop_limit(&self) -> Result { debug!("CALL wasi:sockets/udp#udp-socket.unicast-hop-limit"); self.0.unicast_hop_limit() @@ -1622,6 +1715,70 @@ impl GuestUdpSocket for UdpSocket { debug!("CALL wasi:sockets/udp#udp-socket.subscribe"); Resource::new(Pollable::Host(self.0.subscribe())) } + fn stream( + &self, + remote_addr: Option, + ) -> Result< + ( + Resource, + Resource, + ), + NetworkErrorCode, + > { + debug!("CALL wasi:sockets/udp#udp-socket.stream"); + let (in_, out) = self.0.stream(remote_addr)?; + Ok(( + Resource::new(SocketsIncomingDatagramStream(in_)), + Resource::new(SocketsOutgoingDatagramStream(out)), + )) + } +} + +impl GuestIncomingDatagramStream for SocketsIncomingDatagramStream { + fn receive(&self, max_results: u64) -> Result, NetworkErrorCode> { + debug!("CALL wasi:sockets/udp#incoming-datagram-stream.receive"); + match self.0.receive(max_results) { + Ok(mut datagrams) => Ok(datagrams + .drain(..) + .map(|d| IncomingDatagram { + data: d.data, + remote_address: d.remote_address, + }) + .collect::>()), + Err(err) => Err(err), + } + } + + fn subscribe(&self) -> Resource { + debug!("CALL wasi:sockets/udp#incoming-datagram-stream.subscribe"); + Resource::new(Pollable::Host(self.0.subscribe())) + } +} + +impl GuestOutgoingDatagramStream for SocketsOutgoingDatagramStream { + fn check_send(&self) -> Result { + debug!("CALL wasi:sockets/udp#outgoing-datagram-stream.check-send"); + self.0.check_send() + } + + fn send(&self, mut datagrams: Vec) -> Result { + debug!("CALL wasi:sockets/udp#outgoing-datagram-stream.send"); + self.0.send( + datagrams + .drain(..) + .map(|d| udp::OutgoingDatagram { + data: d.data, + remote_address: d.remote_address, + }) + .collect::>() + .as_slice(), + ) + } + + fn subscribe(&self) -> Resource { + debug!("CALL wasi:sockets/udp#outgoing-datagram-stream.subscribe"); + Resource::new(Pollable::Host(self.0.subscribe())) + } } fn descriptor_ty_map(d: filesystem_types::DescriptorType) -> DescriptorType { @@ -1722,6 +1879,14 @@ fn scheme_map_rev(scheme: http_types::Scheme) -> Scheme { } } +fn header_err_map_rev(err: http_types::HeaderError) -> HeaderError { + match err { + http_types::HeaderError::InvalidSyntax => HeaderError::InvalidSyntax, + http_types::HeaderError::Forbidden => HeaderError::Forbidden, + http_types::HeaderError::Immutable => HeaderError::Immutable, + } +} + fn method_map_rev(method: http_types::Method) -> Method { match method { http_types::Method::Get => Method::Get, @@ -1752,29 +1917,189 @@ fn method_map(method: Method) -> http_types::Method { } } -fn http_err_map(err: HttpError) -> http_types::Error { +fn http_err_map(err: HttpErrorCode) -> http_types::ErrorCode { match err { - HttpError::InvalidUrl(s) => http_types::Error::InvalidUrl(s), - HttpError::TimeoutError(s) => http_types::Error::TimeoutError(s), - HttpError::ProtocolError(s) => http_types::Error::ProtocolError(s), - HttpError::UnexpectedError(s) => http_types::Error::UnexpectedError(s), + HttpErrorCode::DnsTimeout => http_types::ErrorCode::DnsTimeout, + HttpErrorCode::DnsError(DnsErrorPayload { rcode, info_code }) => { + http_types::ErrorCode::DnsError(http_types::DnsErrorPayload { rcode, info_code }) + } + HttpErrorCode::DestinationNotFound => http_types::ErrorCode::DestinationNotFound, + HttpErrorCode::DestinationUnavailable => http_types::ErrorCode::DestinationUnavailable, + HttpErrorCode::DestinationIpProhibited => http_types::ErrorCode::DestinationIpProhibited, + HttpErrorCode::DestinationIpUnroutable => http_types::ErrorCode::DestinationIpUnroutable, + HttpErrorCode::ConnectionRefused => http_types::ErrorCode::ConnectionRefused, + HttpErrorCode::ConnectionTerminated => http_types::ErrorCode::ConnectionTerminated, + HttpErrorCode::ConnectionTimeout => http_types::ErrorCode::ConnectionTimeout, + HttpErrorCode::ConnectionReadTimeout => http_types::ErrorCode::ConnectionReadTimeout, + HttpErrorCode::ConnectionWriteTimeout => http_types::ErrorCode::ConnectionWriteTimeout, + HttpErrorCode::ConnectionLimitReached => http_types::ErrorCode::ConnectionLimitReached, + HttpErrorCode::TlsProtocolError => http_types::ErrorCode::TlsProtocolError, + HttpErrorCode::TlsCertificateError => http_types::ErrorCode::TlsCertificateError, + HttpErrorCode::TlsAlertReceived(TlsAlertReceivedPayload { + alert_id, + alert_message, + }) => http_types::ErrorCode::TlsAlertReceived(http_types::TlsAlertReceivedPayload { + alert_id, + alert_message, + }), + HttpErrorCode::HttpRequestDenied => http_types::ErrorCode::HttpRequestDenied, + HttpErrorCode::HttpRequestLengthRequired => { + http_types::ErrorCode::HttpRequestLengthRequired + } + HttpErrorCode::HttpRequestBodySize(s) => http_types::ErrorCode::HttpRequestBodySize(s), + HttpErrorCode::HttpRequestMethodInvalid => http_types::ErrorCode::HttpRequestMethodInvalid, + HttpErrorCode::HttpRequestUriInvalid => http_types::ErrorCode::HttpRequestUriInvalid, + HttpErrorCode::HttpRequestUriTooLong => http_types::ErrorCode::HttpRequestUriTooLong, + HttpErrorCode::HttpRequestHeaderSectionSize(s) => { + http_types::ErrorCode::HttpRequestHeaderSectionSize(s) + } + HttpErrorCode::HttpRequestHeaderSize(Some(FieldSizePayload { + field_name, + field_size, + })) => http_types::ErrorCode::HttpRequestHeaderSize(Some(http_types::FieldSizePayload { + field_name, + field_size, + })), + HttpErrorCode::HttpRequestHeaderSize(None) => { + http_types::ErrorCode::HttpRequestHeaderSize(None) + } + HttpErrorCode::HttpRequestTrailerSectionSize(s) => { + http_types::ErrorCode::HttpRequestTrailerSectionSize(s) + } + HttpErrorCode::HttpRequestTrailerSize(FieldSizePayload { + field_name, + field_size, + }) => http_types::ErrorCode::HttpRequestTrailerSize(http_types::FieldSizePayload { + field_name, + field_size, + }), + HttpErrorCode::HttpResponseIncomplete => http_types::ErrorCode::HttpResponseIncomplete, + HttpErrorCode::HttpResponseHeaderSectionSize(s) => { + http_types::ErrorCode::HttpResponseHeaderSectionSize(s) + } + HttpErrorCode::HttpResponseHeaderSize(FieldSizePayload { + field_name, + field_size, + }) => http_types::ErrorCode::HttpResponseHeaderSize(http_types::FieldSizePayload { + field_name, + field_size, + }), + HttpErrorCode::HttpResponseBodySize(s) => http_types::ErrorCode::HttpResponseBodySize(s), + HttpErrorCode::HttpResponseTrailerSectionSize(s) => { + http_types::ErrorCode::HttpResponseTrailerSectionSize(s) + } + HttpErrorCode::HttpResponseTrailerSize(FieldSizePayload { + field_name, + field_size, + }) => http_types::ErrorCode::HttpResponseTrailerSize(http_types::FieldSizePayload { + field_name, + field_size, + }), + HttpErrorCode::HttpResponseTransferCoding(e) => { + http_types::ErrorCode::HttpResponseTransferCoding(e) + } + HttpErrorCode::HttpResponseContentCoding(e) => { + http_types::ErrorCode::HttpResponseContentCoding(e) + } + HttpErrorCode::HttpResponseTimeout => http_types::ErrorCode::HttpResponseTimeout, + HttpErrorCode::HttpUpgradeFailed => http_types::ErrorCode::HttpUpgradeFailed, + HttpErrorCode::HttpProtocolError => http_types::ErrorCode::HttpProtocolError, + HttpErrorCode::LoopDetected => http_types::ErrorCode::LoopDetected, + HttpErrorCode::ConfigurationError => http_types::ErrorCode::ConfigurationError, + HttpErrorCode::InternalError(e) => http_types::ErrorCode::InternalError(e), } } -fn http_err_map_rev(err: http_types::Error) -> HttpError { +fn http_err_map_rev(err: http_types::ErrorCode) -> HttpErrorCode { match err { - http_types::Error::InvalidUrl(s) => HttpError::InvalidUrl(s), - http_types::Error::TimeoutError(s) => HttpError::TimeoutError(s), - http_types::Error::ProtocolError(s) => HttpError::ProtocolError(s), - http_types::Error::UnexpectedError(s) => HttpError::UnexpectedError(s), - } -} - -fn request_options_map(options: RequestOptions) -> http_types::RequestOptions { - http_types::RequestOptions { - connect_timeout_ms: options.connect_timeout_ms, - first_byte_timeout_ms: options.first_byte_timeout_ms, - between_bytes_timeout_ms: options.between_bytes_timeout_ms, + http_types::ErrorCode::DnsTimeout => HttpErrorCode::DnsTimeout, + http_types::ErrorCode::DnsError(http_types::DnsErrorPayload { rcode, info_code }) => { + HttpErrorCode::DnsError(DnsErrorPayload { rcode, info_code }) + } + http_types::ErrorCode::DestinationNotFound => HttpErrorCode::DestinationNotFound, + http_types::ErrorCode::DestinationUnavailable => HttpErrorCode::DestinationUnavailable, + http_types::ErrorCode::DestinationIpProhibited => HttpErrorCode::DestinationIpProhibited, + http_types::ErrorCode::DestinationIpUnroutable => HttpErrorCode::DestinationIpUnroutable, + http_types::ErrorCode::ConnectionRefused => HttpErrorCode::ConnectionRefused, + http_types::ErrorCode::ConnectionTerminated => HttpErrorCode::ConnectionTerminated, + http_types::ErrorCode::ConnectionTimeout => HttpErrorCode::ConnectionTimeout, + http_types::ErrorCode::ConnectionReadTimeout => HttpErrorCode::ConnectionReadTimeout, + http_types::ErrorCode::ConnectionWriteTimeout => HttpErrorCode::ConnectionWriteTimeout, + http_types::ErrorCode::ConnectionLimitReached => HttpErrorCode::ConnectionLimitReached, + http_types::ErrorCode::TlsProtocolError => HttpErrorCode::TlsProtocolError, + http_types::ErrorCode::TlsCertificateError => HttpErrorCode::TlsCertificateError, + http_types::ErrorCode::TlsAlertReceived(http_types::TlsAlertReceivedPayload { + alert_id, + alert_message, + }) => HttpErrorCode::TlsAlertReceived(TlsAlertReceivedPayload { + alert_id, + alert_message, + }), + http_types::ErrorCode::HttpRequestDenied => HttpErrorCode::HttpRequestDenied, + http_types::ErrorCode::HttpRequestLengthRequired => { + HttpErrorCode::HttpRequestLengthRequired + } + http_types::ErrorCode::HttpRequestBodySize(s) => HttpErrorCode::HttpRequestBodySize(s), + http_types::ErrorCode::HttpRequestMethodInvalid => HttpErrorCode::HttpRequestMethodInvalid, + http_types::ErrorCode::HttpRequestUriInvalid => HttpErrorCode::HttpRequestUriInvalid, + http_types::ErrorCode::HttpRequestUriTooLong => HttpErrorCode::HttpRequestUriTooLong, + http_types::ErrorCode::HttpRequestHeaderSectionSize(s) => { + HttpErrorCode::HttpRequestHeaderSectionSize(s) + } + http_types::ErrorCode::HttpRequestHeaderSize(Some(http_types::FieldSizePayload { + field_name, + field_size, + })) => HttpErrorCode::HttpRequestHeaderSize(Some(FieldSizePayload { + field_name, + field_size, + })), + http_types::ErrorCode::HttpRequestHeaderSize(None) => { + HttpErrorCode::HttpRequestHeaderSize(None) + } + http_types::ErrorCode::HttpRequestTrailerSectionSize(s) => { + HttpErrorCode::HttpRequestTrailerSectionSize(s) + } + http_types::ErrorCode::HttpRequestTrailerSize(http_types::FieldSizePayload { + field_name, + field_size, + }) => HttpErrorCode::HttpRequestTrailerSize(FieldSizePayload { + field_name, + field_size, + }), + http_types::ErrorCode::HttpResponseIncomplete => HttpErrorCode::HttpResponseIncomplete, + http_types::ErrorCode::HttpResponseHeaderSectionSize(s) => { + HttpErrorCode::HttpResponseHeaderSectionSize(s) + } + http_types::ErrorCode::HttpResponseHeaderSize(http_types::FieldSizePayload { + field_name, + field_size, + }) => HttpErrorCode::HttpResponseHeaderSize(FieldSizePayload { + field_name, + field_size, + }), + http_types::ErrorCode::HttpResponseBodySize(s) => HttpErrorCode::HttpResponseBodySize(s), + http_types::ErrorCode::HttpResponseTrailerSectionSize(s) => { + HttpErrorCode::HttpResponseTrailerSectionSize(s) + } + http_types::ErrorCode::HttpResponseTrailerSize(http_types::FieldSizePayload { + field_name, + field_size, + }) => HttpErrorCode::HttpResponseTrailerSize(FieldSizePayload { + field_name, + field_size, + }), + http_types::ErrorCode::HttpResponseTransferCoding(e) => { + HttpErrorCode::HttpResponseTransferCoding(e) + } + http_types::ErrorCode::HttpResponseContentCoding(e) => { + HttpErrorCode::HttpResponseContentCoding(e) + } + http_types::ErrorCode::HttpResponseTimeout => HttpErrorCode::HttpResponseTimeout, + http_types::ErrorCode::HttpUpgradeFailed => HttpErrorCode::HttpUpgradeFailed, + http_types::ErrorCode::HttpProtocolError => HttpErrorCode::HttpProtocolError, + http_types::ErrorCode::LoopDetected => HttpErrorCode::LoopDetected, + http_types::ErrorCode::ConfigurationError => HttpErrorCode::ConfigurationError, + http_types::ErrorCode::InternalError(e) => HttpErrorCode::InternalError(e), } } diff --git a/virtual-adapter/src/lib.rs b/virtual-adapter/src/lib.rs index 7f57d3d..3b69025 100644 --- a/virtual-adapter/src/lib.rs +++ b/virtual-adapter/src/lib.rs @@ -10,25 +10,22 @@ wit_bindgen::generate!({ path: "../wit", world: "virtual-adapter", exports: { - "wasi:io/poll": VirtAdapter, - "wasi:io/poll/pollable": io::IoPollable, - "wasi:io/error/error": io::IoError, - "wasi:io/streams/input-stream": io::IoInputStream, - "wasi:io/streams/output-stream": io::IoOutputStream, - "wasi:filesystem/preopens": VirtAdapter, - "wasi:filesystem/types": VirtAdapter, - "wasi:filesystem/types/descriptor": io::FilesystemDescriptor, - "wasi:filesystem/types/directory-entry-stream": io::FilesystemDirectoryEntryStream, "wasi:cli/environment": VirtAdapter, + "wasi:cli/stderr": VirtAdapter, "wasi:cli/stdin": VirtAdapter, "wasi:cli/stdout": VirtAdapter, - "wasi:cli/stderr": VirtAdapter, "wasi:cli/terminal-input/terminal-input": io::CliTerminalInput, "wasi:cli/terminal-output/terminal-output": io::CliTerminalOutput, + "wasi:cli/terminal-stderr": VirtAdapter, "wasi:cli/terminal-stdin": VirtAdapter, "wasi:cli/terminal-stdout": VirtAdapter, - "wasi:cli/terminal-stderr": VirtAdapter, "wasi:clocks/monotonic-clock": VirtAdapter, + "wasi:filesystem/preopens": VirtAdapter, + "wasi:filesystem/types": VirtAdapter, + "wasi:filesystem/types/descriptor": io::FilesystemDescriptor, + "wasi:filesystem/types/directory-entry-stream": io::FilesystemDirectoryEntryStream, + "wasi:http/outgoing-handler": VirtAdapter, + "wasi:http/types": io::HttpTypes, "wasi:http/types/fields": io::HttpFields, "wasi:http/types/future-incoming-response": io::HttpFutureIncomingResponse, "wasi:http/types/future-trailers": io::HttpFutureTrailers, @@ -38,11 +35,18 @@ wit_bindgen::generate!({ "wasi:http/types/outgoing-body": io::HttpOutgoingBody, "wasi:http/types/outgoing-request": io::HttpOutgoingRequest, "wasi:http/types/outgoing-response": io::HttpOutgoingResponse, + "wasi:http/types/request-options": io::HttpRequestOptions, "wasi:http/types/response-outparam": io::HttpResponseOutparam, - "wasi:http/outgoing-handler": VirtAdapter, + "wasi:io/error/error": io::IoError, + "wasi:io/poll": VirtAdapter, + "wasi:io/poll/pollable": io::IoPollable, + "wasi:io/streams/input-stream": io::IoInputStream, + "wasi:io/streams/output-stream": io::IoOutputStream, "wasi:sockets/ip-name-lookup": VirtAdapter, "wasi:sockets/ip-name-lookup/resolve-address-stream": io::SocketsResolveAddressStream, "wasi:sockets/tcp/tcp-socket": io::SocketsTcpSocket, + "wasi:sockets/udp/incoming-datagram-stream": io::SocketsIncomingDatagramStream, + "wasi:sockets/udp/outgoing-datagram-stream": io::SocketsOutgoingDatagramStream, "wasi:sockets/udp/udp-socket": io::SocketsUdpSocket, } }); diff --git a/wit/deps/cli/command.wit b/wit/deps/cli/command.wit index cc82ae5..d8005bd 100644 --- a/wit/deps/cli/command.wit +++ b/wit/deps/cli/command.wit @@ -1,4 +1,4 @@ -package wasi:cli@0.2.0-rc-2023-12-05; +package wasi:cli@0.2.0; world command { include imports; diff --git a/wit/deps/cli/imports.wit b/wit/deps/cli/imports.wit index 36b1c59..083b84a 100644 --- a/wit/deps/cli/imports.wit +++ b/wit/deps/cli/imports.wit @@ -1,11 +1,11 @@ -package wasi:cli@0.2.0-rc-2023-12-05; +package wasi:cli@0.2.0; world imports { - include wasi:clocks/imports@0.2.0-rc-2023-11-10; - include wasi:filesystem/imports@0.2.0-rc-2023-11-10; - include wasi:sockets/imports@0.2.0-rc-2023-10-18; - include wasi:random/imports@0.2.0-rc-2023-11-10; - include wasi:io/imports@0.2.0-rc-2023-11-10; + include wasi:clocks/imports@0.2.0; + include wasi:filesystem/imports@0.2.0; + include wasi:sockets/imports@0.2.0; + include wasi:random/imports@0.2.0; + include wasi:io/imports@0.2.0; import environment; import exit; diff --git a/wit/deps/cli/stdio.wit b/wit/deps/cli/stdio.wit index 1b653b6..31ef35b 100644 --- a/wit/deps/cli/stdio.wit +++ b/wit/deps/cli/stdio.wit @@ -1,17 +1,17 @@ interface stdin { - use wasi:io/streams@0.2.0-rc-2023-11-10.{input-stream}; + use wasi:io/streams@0.2.0.{input-stream}; get-stdin: func() -> input-stream; } interface stdout { - use wasi:io/streams@0.2.0-rc-2023-11-10.{output-stream}; + use wasi:io/streams@0.2.0.{output-stream}; get-stdout: func() -> output-stream; } interface stderr { - use wasi:io/streams@0.2.0-rc-2023-11-10.{output-stream}; + use wasi:io/streams@0.2.0.{output-stream}; get-stderr: func() -> output-stream; } diff --git a/wit/deps/cli/terminal.wit b/wit/deps/cli/terminal.wit index 4749576..38c724e 100644 --- a/wit/deps/cli/terminal.wit +++ b/wit/deps/cli/terminal.wit @@ -1,19 +1,21 @@ +/// Terminal input. +/// +/// In the future, this may include functions for disabling echoing, +/// disabling input buffering so that keyboard events are sent through +/// immediately, querying supported features, and so on. interface terminal-input { /// The input side of a terminal. resource terminal-input; - - // In the future, this may include functions for disabling echoing, - // disabling input buffering so that keyboard events are sent through - // immediately, querying supported features, and so on. } +/// Terminal output. +/// +/// In the future, this may include functions for querying the terminal +/// size, being notified of terminal size changes, querying supported +/// features, and so on. interface terminal-output { /// The output side of a terminal. resource terminal-output; - - // In the future, this may include functions for querying the terminal - // size, being notified of terminal size changes, querying supported - // features, and so on. } /// An interface providing an optional `terminal-input` for stdin as a diff --git a/wit/deps/clocks/monotonic-clock.wit b/wit/deps/clocks/monotonic-clock.wit index 09ef32c..4e4dc3a 100644 --- a/wit/deps/clocks/monotonic-clock.wit +++ b/wit/deps/clocks/monotonic-clock.wit @@ -1,4 +1,4 @@ -package wasi:clocks@0.2.0-rc-2023-11-10; +package wasi:clocks@0.2.0; /// WASI Monotonic Clock is a clock API intended to let users measure elapsed /// time. /// @@ -10,7 +10,7 @@ package wasi:clocks@0.2.0-rc-2023-11-10; /// /// It is intended for measuring elapsed time. interface monotonic-clock { - use wasi:io/poll@0.2.0-rc-2023-11-10.{pollable}; + use wasi:io/poll@0.2.0.{pollable}; /// An instant in time, in nanoseconds. An instant is relative to an /// unspecified initial value, and can only be compared to instances from diff --git a/wit/deps/clocks/wall-clock.wit b/wit/deps/clocks/wall-clock.wit index 8abb9a0..440ca0f 100644 --- a/wit/deps/clocks/wall-clock.wit +++ b/wit/deps/clocks/wall-clock.wit @@ -1,4 +1,4 @@ -package wasi:clocks@0.2.0-rc-2023-11-10; +package wasi:clocks@0.2.0; /// WASI Wall Clock is a clock API intended to let users query the current /// time. The name "wall" makes an analogy to a "clock on the wall", which /// is not necessarily monotonic as it may be reset. diff --git a/wit/deps/clocks/world.wit b/wit/deps/clocks/world.wit index 8fa080f..c022457 100644 --- a/wit/deps/clocks/world.wit +++ b/wit/deps/clocks/world.wit @@ -1,4 +1,4 @@ -package wasi:clocks@0.2.0-rc-2023-11-10; +package wasi:clocks@0.2.0; world imports { import monotonic-clock; diff --git a/wit/deps/filesystem/preopens.wit b/wit/deps/filesystem/preopens.wit index 95ec678..da801f6 100644 --- a/wit/deps/filesystem/preopens.wit +++ b/wit/deps/filesystem/preopens.wit @@ -1,4 +1,4 @@ -package wasi:filesystem@0.2.0-rc-2023-11-10; +package wasi:filesystem@0.2.0; interface preopens { use types.{descriptor}; diff --git a/wit/deps/filesystem/types.wit b/wit/deps/filesystem/types.wit index 059722a..11108fc 100644 --- a/wit/deps/filesystem/types.wit +++ b/wit/deps/filesystem/types.wit @@ -1,4 +1,4 @@ -package wasi:filesystem@0.2.0-rc-2023-11-10; +package wasi:filesystem@0.2.0; /// WASI filesystem is a filesystem API primarily intended to let users run WASI /// programs that access their files on their existing filesystems, without /// significant overhead. @@ -24,8 +24,8 @@ package wasi:filesystem@0.2.0-rc-2023-11-10; /// /// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md interface types { - use wasi:io/streams@0.2.0-rc-2023-11-10.{input-stream, output-stream, error}; - use wasi:clocks/wall-clock@0.2.0-rc-2023-11-10.{datetime}; + use wasi:io/streams@0.2.0.{input-stream, output-stream, error}; + use wasi:clocks/wall-clock@0.2.0.{datetime}; /// File size or length of a region within a file. type filesize = u64; diff --git a/wit/deps/filesystem/world.wit b/wit/deps/filesystem/world.wit index 285e0ba..663f579 100644 --- a/wit/deps/filesystem/world.wit +++ b/wit/deps/filesystem/world.wit @@ -1,4 +1,4 @@ -package wasi:filesystem@0.2.0-rc-2023-11-10; +package wasi:filesystem@0.2.0; world imports { import types; diff --git a/wit/deps/http/handler.wit b/wit/deps/http/handler.wit new file mode 100644 index 0000000..a34a064 --- /dev/null +++ b/wit/deps/http/handler.wit @@ -0,0 +1,43 @@ +/// This interface defines a handler of incoming HTTP Requests. It should +/// be exported by components which can respond to HTTP Requests. +interface incoming-handler { + use types.{incoming-request, response-outparam}; + + /// This function is invoked with an incoming HTTP Request, and a resource + /// `response-outparam` which provides the capability to reply with an HTTP + /// Response. The response is sent by calling the `response-outparam.set` + /// method, which allows execution to continue after the response has been + /// sent. This enables both streaming to the response body, and performing other + /// work. + /// + /// The implementor of this function must write a response to the + /// `response-outparam` before returning, or else the caller will respond + /// with an error on its behalf. + handle: func( + request: incoming-request, + response-out: response-outparam + ); +} + +/// This interface defines a handler of outgoing HTTP Requests. It should be +/// imported by components which wish to make HTTP Requests. +interface outgoing-handler { + use types.{ + outgoing-request, request-options, future-incoming-response, error-code + }; + + /// This function is invoked with an outgoing HTTP Request, and it returns + /// a resource `future-incoming-response` which represents an HTTP Response + /// which may arrive in the future. + /// + /// The `options` argument accepts optional parameters for the HTTP + /// protocol's transport layer. + /// + /// This function may return an error if the `outgoing-request` is invalid + /// or not allowed to be made. Otherwise, protocol errors are reported + /// through the `future-incoming-response`. + handle: func( + request: outgoing-request, + options: option + ) -> result; +} diff --git a/wit/deps/http/incoming-handler.wit b/wit/deps/http/incoming-handler.wit deleted file mode 100644 index 6968d63..0000000 --- a/wit/deps/http/incoming-handler.wit +++ /dev/null @@ -1,24 +0,0 @@ -// The `wasi:http/incoming-handler` interface is meant to be exported by -// components and called by the host in response to a new incoming HTTP -// response. -// -// NOTE: in Preview3, this interface will be merged with -// `wasi:http/outgoing-handler` into a single `wasi:http/handler` interface -// that takes a `request` parameter and returns a `response` result. -// -interface incoming-handler { - use types.{incoming-request, response-outparam}; - - // The `handle` function takes an outparam instead of returning its response - // so that the component may stream its response while streaming any other - // request or response bodies. The callee MUST write a response to the - // `response-outparam` and then finish the response before returning. The `handle` - // function is allowed to continue execution after finishing the response's - // output stream. While this post-response execution is taken off the - // critical path, since there is no return value, there is no way to report - // its success or failure. - handle: func( - request: incoming-request, - response-out: response-outparam - ); -} diff --git a/wit/deps/http/outgoing-handler.wit b/wit/deps/http/outgoing-handler.wit deleted file mode 100644 index 286e283..0000000 --- a/wit/deps/http/outgoing-handler.wit +++ /dev/null @@ -1,20 +0,0 @@ -// The `wasi:http/outgoing-handler` interface is meant to be imported by -// components and implemented by the host. -// -// NOTE: in Preview3, this interface will be merged with -// `wasi:http/outgoing-handler` into a single `wasi:http/handler` interface -// that takes a `request` parameter and returns a `response` result. -// -interface outgoing-handler { - use types.{outgoing-request, request-options, future-incoming-response, error}; - - // The parameter and result types of the `handle` function allow the caller - // to concurrently stream the bodies of the outgoing request and the incoming - // response. - // Consumes the outgoing-request. Gives an error if the outgoing-request - // is invalid or cannot be satisfied by this handler. - handle: func( - request: outgoing-request, - options: option - ) -> result; -} diff --git a/wit/deps/http/proxy.wit b/wit/deps/http/proxy.wit index 20d4a43..687c24d 100644 --- a/wit/deps/http/proxy.wit +++ b/wit/deps/http/proxy.wit @@ -1,33 +1,32 @@ -package wasi:http@0.2.0-rc-2023-10-18; +package wasi:http@0.2.0; -// The `wasi:http/proxy` world captures a widely-implementable intersection of -// hosts that includes HTTP forward and reverse proxies. Components targeting -// this world may concurrently stream in and out any number of incoming and -// outgoing HTTP requests. +/// The `wasi:http/proxy` world captures a widely-implementable intersection of +/// hosts that includes HTTP forward and reverse proxies. Components targeting +/// this world may concurrently stream in and out any number of incoming and +/// outgoing HTTP requests. world proxy { - // HTTP proxies have access to time and randomness. - import wasi:clocks/wall-clock@0.2.0-rc-2023-11-10; - import wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10; - import wasi:random/random@0.2.0-rc-2023-11-10; + /// HTTP proxies have access to time and randomness. + include wasi:clocks/imports@0.2.0; + import wasi:random/random@0.2.0; - // Proxies have standard output and error streams which are expected to - // terminate in a developer-facing console provided by the host. - import wasi:cli/stdout@0.2.0-rc-2023-12-05; - import wasi:cli/stderr@0.2.0-rc-2023-12-05; + /// Proxies have standard output and error streams which are expected to + /// terminate in a developer-facing console provided by the host. + import wasi:cli/stdout@0.2.0; + import wasi:cli/stderr@0.2.0; - // TODO: this is a temporary workaround until component tooling is able to - // gracefully handle the absence of stdin. Hosts must return an eof stream - // for this import, which is what wasi-libc + tooling will do automatically - // when this import is properly removed. - import wasi:cli/stdin@0.2.0-rc-2023-12-05; + /// TODO: this is a temporary workaround until component tooling is able to + /// gracefully handle the absence of stdin. Hosts must return an eof stream + /// for this import, which is what wasi-libc + tooling will do automatically + /// when this import is properly removed. + import wasi:cli/stdin@0.2.0; - // This is the default handler to use when user code simply wants to make an - // HTTP request (e.g., via `fetch()`). + /// This is the default handler to use when user code simply wants to make an + /// HTTP request (e.g., via `fetch()`). import outgoing-handler; - // The host delivers incoming HTTP requests to a component by calling the - // `handle` function of this exported interface. A host may arbitrarily reuse - // or not reuse component instance when delivering incoming HTTP requests and - // thus a component must be able to handle 0..N calls to `handle`. + /// The host delivers incoming HTTP requests to a component by calling the + /// `handle` function of this exported interface. A host may arbitrarily reuse + /// or not reuse component instance when delivering incoming HTTP requests and + /// thus a component must be able to handle 0..N calls to `handle`. export incoming-handler; } diff --git a/wit/deps/http/types.wit b/wit/deps/http/types.wit index 5f604a6..755ac6a 100644 --- a/wit/deps/http/types.wit +++ b/wit/deps/http/types.wit @@ -1,11 +1,13 @@ -// The `wasi:http/types` interface is meant to be imported by components to -// define the HTTP resource types and operations used by the component's -// imported and exported interfaces. +/// This interface defines all of the types and methods for implementing +/// HTTP Requests and Responses, both incoming and outgoing, as well as +/// their headers, trailers, and bodies. interface types { - use wasi:io/streams@0.2.0-rc-2023-11-10.{input-stream, output-stream}; - use wasi:io/poll@0.2.0-rc-2023-11-10.{pollable}; + use wasi:clocks/monotonic-clock@0.2.0.{duration}; + use wasi:io/streams@0.2.0.{input-stream, output-stream}; + use wasi:io/error@0.2.0.{error as io-error}; + use wasi:io/poll@0.2.0.{pollable}; - // This type corresponds to HTTP standard Methods. + /// This type corresponds to HTTP standard Methods. variant method { get, head, @@ -19,196 +21,550 @@ interface types { other(string) } - // This type corresponds to HTTP standard Related Schemes. + /// This type corresponds to HTTP standard Related Schemes. variant scheme { HTTP, HTTPS, other(string) } - // TODO: perhaps better align with HTTP semantics? - // This type enumerates the different kinds of errors that may occur when - // initially returning a response. - variant error { - invalid-url(string), - timeout-error(string), - protocol-error(string), - unexpected-error(string) + /// These cases are inspired by the IANA HTTP Proxy Error Types: + /// https://www.iana.org/assignments/http-proxy-status/http-proxy-status.xhtml#table-http-proxy-error-types + variant error-code { + DNS-timeout, + DNS-error(DNS-error-payload), + destination-not-found, + destination-unavailable, + destination-IP-prohibited, + destination-IP-unroutable, + connection-refused, + connection-terminated, + connection-timeout, + connection-read-timeout, + connection-write-timeout, + connection-limit-reached, + TLS-protocol-error, + TLS-certificate-error, + TLS-alert-received(TLS-alert-received-payload), + HTTP-request-denied, + HTTP-request-length-required, + HTTP-request-body-size(option), + HTTP-request-method-invalid, + HTTP-request-URI-invalid, + HTTP-request-URI-too-long, + HTTP-request-header-section-size(option), + HTTP-request-header-size(option), + HTTP-request-trailer-section-size(option), + HTTP-request-trailer-size(field-size-payload), + HTTP-response-incomplete, + HTTP-response-header-section-size(option), + HTTP-response-header-size(field-size-payload), + HTTP-response-body-size(option), + HTTP-response-trailer-section-size(option), + HTTP-response-trailer-size(field-size-payload), + HTTP-response-transfer-coding(option), + HTTP-response-content-coding(option), + HTTP-response-timeout, + HTTP-upgrade-failed, + HTTP-protocol-error, + loop-detected, + configuration-error, + /// This is a catch-all error for anything that doesn't fit cleanly into a + /// more specific case. It also includes an optional string for an + /// unstructured description of the error. Users should not depend on the + /// string for diagnosing errors, as it's not required to be consistent + /// between implementations. + internal-error(option) } - // This following block defines the `fields` resource which corresponds to - // HTTP standard Fields. Soon, when resource types are added, the `type - // fields = u32` type alias can be replaced by a proper `resource fields` - // definition containing all the functions using the method syntactic sugar. - resource fields { - // Multiple values for a header are multiple entries in the list with the - // same key. - constructor(entries: list>>); + /// Defines the case payload type for `DNS-error` above: + record DNS-error-payload { + rcode: option, + info-code: option + } + + /// Defines the case payload type for `TLS-alert-received` above: + record TLS-alert-received-payload { + alert-id: option, + alert-message: option + } - // Values off wire are not necessarily well formed, so they are given by - // list instead of string. - get: func(name: string) -> list>; + /// Defines the case payload type for `HTTP-response-{header,trailer}-size` above: + record field-size-payload { + field-name: option, + field-size: option + } - // Values off wire are not necessarily well formed, so they are given by - // list instead of string. - set: func(name: string, value: list>); - delete: func(name: string); - append: func(name: string, value: list); + /// Attempts to extract a http-related `error` from the wasi:io `error` + /// provided. + /// + /// Stream operations which return + /// `wasi:io/stream/stream-error::last-operation-failed` have a payload of + /// type `wasi:io/error/error` with more information about the operation + /// that failed. This payload can be passed through to this function to see + /// if there's http-related information about the error to return. + /// + /// Note that this function is fallible because not all io-errors are + /// http-related errors. + http-error-code: func(err: borrow) -> option; + + /// This type enumerates the different kinds of errors that may occur when + /// setting or appending to a `fields` resource. + variant header-error { + /// This error indicates that a `field-key` or `field-value` was + /// syntactically invalid when used with an operation that sets headers in a + /// `fields`. + invalid-syntax, + + /// This error indicates that a forbidden `field-key` was used when trying + /// to set a header in a `fields`. + forbidden, + + /// This error indicates that the operation on the `fields` was not + /// permitted because the fields are immutable. + immutable, + } - // Values off wire are not necessarily well formed, so they are given by - // list instead of string. - entries: func() -> list>>; + /// Field keys are always strings. + type field-key = string; + + /// Field values should always be ASCII strings. However, in + /// reality, HTTP implementations often have to interpret malformed values, + /// so they are provided as a list of bytes. + type field-value = list; + + /// This following block defines the `fields` resource which corresponds to + /// HTTP standard Fields. Fields are a common representation used for both + /// Headers and Trailers. + /// + /// A `fields` may be mutable or immutable. A `fields` created using the + /// constructor, `from-list`, or `clone` will be mutable, but a `fields` + /// resource given by other means (including, but not limited to, + /// `incoming-request.headers`, `outgoing-request.headers`) might be be + /// immutable. In an immutable fields, the `set`, `append`, and `delete` + /// operations will fail with `header-error.immutable`. + resource fields { - // Deep copy of all contents in a fields. + /// Construct an empty HTTP Fields. + /// + /// The resulting `fields` is mutable. + constructor(); + + /// Construct an HTTP Fields. + /// + /// The resulting `fields` is mutable. + /// + /// The list represents each key-value pair in the Fields. Keys + /// which have multiple values are represented by multiple entries in this + /// list with the same key. + /// + /// The tuple is a pair of the field key, represented as a string, and + /// Value, represented as a list of bytes. In a valid Fields, all keys + /// and values are valid UTF-8 strings. However, values are not always + /// well-formed, so they are represented as a raw list of bytes. + /// + /// An error result will be returned if any header or value was + /// syntactically invalid, or if a header was forbidden. + from-list: static func( + entries: list> + ) -> result; + + /// Get all of the values corresponding to a key. If the key is not present + /// in this `fields`, an empty list is returned. However, if the key is + /// present but empty, this is represented by a list with one or more + /// empty field-values present. + get: func(name: field-key) -> list; + + /// Returns `true` when the key is present in this `fields`. If the key is + /// syntactically invalid, `false` is returned. + has: func(name: field-key) -> bool; + + /// Set all of the values for a key. Clears any existing values for that + /// key, if they have been set. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + set: func(name: field-key, value: list) -> result<_, header-error>; + + /// Delete all values for a key. Does nothing if no values for the key + /// exist. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + delete: func(name: field-key) -> result<_, header-error>; + + /// Append a value for a key. Does not change or delete any existing + /// values for that key. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + append: func(name: field-key, value: field-value) -> result<_, header-error>; + + /// Retrieve the full set of keys and values in the Fields. Like the + /// constructor, the list represents each key-value pair. + /// + /// The outer list represents each key-value pair in the Fields. Keys + /// which have multiple values are represented by multiple entries in this + /// list with the same key. + entries: func() -> list>; + + /// Make a deep copy of the Fields. Equivelant in behavior to calling the + /// `fields` constructor on the return value of `entries`. The resulting + /// `fields` is mutable. clone: func() -> fields; } + /// Headers is an alias for Fields. type headers = fields; + + /// Trailers is an alias for Fields. type trailers = fields; - // The following block defines the `incoming-request` and `outgoing-request` - // resource types that correspond to HTTP standard Requests. Soon, when - // resource types are added, the `u32` type aliases can be replaced by - // proper `resource` type definitions containing all the functions as - // methods. Later, Preview2 will allow both types to be merged together into - // a single `request` type (that uses the single `stream` type mentioned - // above). The `consume` and `write` methods may only be called once (and - // return failure thereafter). + /// Represents an incoming HTTP Request. resource incoming-request { + + /// Returns the method of the incoming request. method: func() -> method; + /// Returns the path with query parameters from the request, as a string. path-with-query: func() -> option; + /// Returns the protocol scheme from the request. scheme: func() -> option; + /// Returns the authority from the request, if it was present. authority: func() -> option; - headers: func() -> /* child */ headers; - // Will return the input-stream child at most once. If called more than - // once, subsequent calls will return error. - + /// Get the `headers` associated with the request. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + /// + /// The `headers` returned are a child resource: it must be dropped before + /// the parent `incoming-request` is dropped. Dropping this + /// `incoming-request` before all children are dropped will trap. + headers: func() -> headers; + + /// Gives the `incoming-body` associated with this request. Will only + /// return success at most once, and subsequent calls will return error. consume: func() -> result; } + /// Represents an outgoing HTTP Request. resource outgoing-request { + + /// Construct a new `outgoing-request` with a default `method` of `GET`, and + /// `none` values for `path-with-query`, `scheme`, and `authority`. + /// + /// * `headers` is the HTTP Headers for the Request. + /// + /// It is possible to construct, or manipulate with the accessor functions + /// below, an `outgoing-request` with an invalid combination of `scheme` + /// and `authority`, or `headers` which are not permitted to be sent. + /// It is the obligation of the `outgoing-handler.handle` implementation + /// to reject invalid constructions of `outgoing-request`. constructor( - method: method, - path-with-query: option, - scheme: option, - authority: option, - headers: borrow + headers: headers ); - // Will return the outgoing-body child at most once. If called more than - // once, subsequent calls will return error. - write: func() -> result< /* child */ outgoing-body>; - } + /// Returns the resource corresponding to the outgoing Body for this + /// Request. + /// + /// Returns success on the first call: the `outgoing-body` resource for + /// this `outgoing-request` can be retrieved at most once. Subsequent + /// calls will return error. + body: func() -> result; - // Additional optional parameters that can be set when making a request. - record request-options { - // The following timeouts are specific to the HTTP protocol and work - // independently of the overall timeouts passed to `io.poll.poll-list`. + /// Get the Method for the Request. + method: func() -> method; + /// Set the Method for the Request. Fails if the string present in a + /// `method.other` argument is not a syntactically valid method. + set-method: func(method: method) -> result; - // The timeout for the initial connect. - connect-timeout-ms: option, + /// Get the combination of the HTTP Path and Query for the Request. + /// When `none`, this represents an empty Path and empty Query. + path-with-query: func() -> option; + /// Set the combination of the HTTP Path and Query for the Request. + /// When `none`, this represents an empty Path and empty Query. Fails is the + /// string given is not a syntactically valid path and query uri component. + set-path-with-query: func(path-with-query: option) -> result; - // The timeout for receiving the first byte of the response body. - first-byte-timeout-ms: option, + /// Get the HTTP Related Scheme for the Request. When `none`, the + /// implementation may choose an appropriate default scheme. + scheme: func() -> option; + /// Set the HTTP Related Scheme for the Request. When `none`, the + /// implementation may choose an appropriate default scheme. Fails if the + /// string given is not a syntactically valid uri scheme. + set-scheme: func(scheme: option) -> result; + + /// Get the HTTP Authority for the Request. A value of `none` may be used + /// with Related Schemes which do not require an Authority. The HTTP and + /// HTTPS schemes always require an authority. + authority: func() -> option; + /// Set the HTTP Authority for the Request. A value of `none` may be used + /// with Related Schemes which do not require an Authority. The HTTP and + /// HTTPS schemes always require an authority. Fails if the string given is + /// not a syntactically valid uri authority. + set-authority: func(authority: option) -> result; + + /// Get the headers associated with the Request. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + /// + /// This headers resource is a child: it must be dropped before the parent + /// `outgoing-request` is dropped, or its ownership is transfered to + /// another component by e.g. `outgoing-handler.handle`. + headers: func() -> headers; + } - // The timeout for receiving the next chunk of bytes in the response body - // stream. - between-bytes-timeout-ms: option + /// Parameters for making an HTTP Request. Each of these parameters is + /// currently an optional timeout applicable to the transport layer of the + /// HTTP protocol. + /// + /// These timeouts are separate from any the user may use to bound a + /// blocking call to `wasi:io/poll.poll`. + resource request-options { + /// Construct a default `request-options` value. + constructor(); + + /// The timeout for the initial connect to the HTTP Server. + connect-timeout: func() -> option; + + /// Set the timeout for the initial connect to the HTTP Server. An error + /// return value indicates that this timeout is not supported. + set-connect-timeout: func(duration: option) -> result; + + /// The timeout for receiving the first byte of the Response body. + first-byte-timeout: func() -> option; + + /// Set the timeout for receiving the first byte of the Response body. An + /// error return value indicates that this timeout is not supported. + set-first-byte-timeout: func(duration: option) -> result; + + /// The timeout for receiving subsequent chunks of bytes in the Response + /// body stream. + between-bytes-timeout: func() -> option; + + /// Set the timeout for receiving subsequent chunks of bytes in the Response + /// body stream. An error return value indicates that this timeout is not + /// supported. + set-between-bytes-timeout: func(duration: option) -> result; } - // The following block defines a special resource type used by the - // `wasi:http/incoming-handler` interface. When resource types are added, this - // block can be replaced by a proper `resource response-outparam { ... }` - // definition. Later, with Preview3, the need for an outparam goes away entirely - // (the `wasi:http/handler` interface used for both incoming and outgoing can - // simply return a `stream`). + /// Represents the ability to send an HTTP Response. + /// + /// This resource is used by the `wasi:http/incoming-handler` interface to + /// allow a Response to be sent corresponding to the Request provided as the + /// other argument to `incoming-handler.handle`. resource response-outparam { - set: static func(param: response-outparam, response: result); + + /// Set the value of the `response-outparam` to either send a response, + /// or indicate an error. + /// + /// This method consumes the `response-outparam` to ensure that it is + /// called at most once. If it is never called, the implementation + /// will respond with an error. + /// + /// The user may provide an `error` to `response` to allow the + /// implementation determine how to respond with an HTTP error response. + set: static func( + param: response-outparam, + response: result, + ); } - // This type corresponds to the HTTP standard Status Code. + /// This type corresponds to the HTTP standard Status Code. type status-code = u16; - // The following block defines the `incoming-response` and `outgoing-response` - // resource types that correspond to HTTP standard Responses. Soon, when - // resource types are added, the `u32` type aliases can be replaced by proper - // `resource` type definitions containing all the functions as methods. Later, - // Preview2 will allow both types to be merged together into a single `response` - // type (that uses the single `stream` type mentioned above). The `consume` and - // `write` methods may only be called once (and return failure thereafter). + /// Represents an incoming HTTP Response. resource incoming-response { - status: func() -> status-code; - headers: func() -> /* child */ headers; + /// Returns the status code from the incoming response. + status: func() -> status-code; - // May be called at most once. returns error if called additional times. - // TODO: make incoming-request-consume work the same way, giving a child - // incoming-body. + /// Returns the headers from the incoming response. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + /// + /// This headers resource is a child: it must be dropped before the parent + /// `incoming-response` is dropped. + headers: func() -> headers; + + /// Returns the incoming body. May be called at most once. Returns error + /// if called additional times. consume: func() -> result; } + /// Represents an incoming HTTP Request or Response's Body. + /// + /// A body has both its contents - a stream of bytes - and a (possibly + /// empty) set of trailers, indicating that the full contents of the + /// body have been received. This resource represents the contents as + /// an `input-stream` and the delivery of trailers as a `future-trailers`, + /// and ensures that the user of this interface may only be consuming either + /// the body contents or waiting on trailers at any given time. resource incoming-body { - // returned input-stream is a child - the implementation may trap if - // incoming-body is dropped (or consumed by call to - // incoming-body-finish) before the input-stream is dropped. - // May be called at most once. returns error if called additional times. - %stream: func() -> result; - // takes ownership of incoming-body. this will trap if the - // incoming-body-stream child is still alive! - finish: static func(this: incoming-body) -> - /* transitive child of the incoming-response of incoming-body */ future-trailers; + /// Returns the contents of the body, as a stream of bytes. + /// + /// Returns success on first call: the stream representing the contents + /// can be retrieved at most once. Subsequent calls will return error. + /// + /// The returned `input-stream` resource is a child: it must be dropped + /// before the parent `incoming-body` is dropped, or consumed by + /// `incoming-body.finish`. + /// + /// This invariant ensures that the implementation can determine whether + /// the user is consuming the contents of the body, waiting on the + /// `future-trailers` to be ready, or neither. This allows for network + /// backpressure is to be applied when the user is consuming the body, + /// and for that backpressure to not inhibit delivery of the trailers if + /// the user does not read the entire body. + %stream: func() -> result; + + /// Takes ownership of `incoming-body`, and returns a `future-trailers`. + /// This function will trap if the `input-stream` child is still alive. + finish: static func(this: incoming-body) -> future-trailers; } + /// Represents a future which may eventaully return trailers, or an error. + /// + /// In the case that the incoming HTTP Request or Response did not have any + /// trailers, this future will resolve to the empty set of trailers once the + /// complete Request or Response body has been received. resource future-trailers { - /// Pollable that resolves when the body has been fully read, and the trailers - /// are ready to be consumed. - subscribe: func() -> /* child */ pollable; - /// Retrieve reference to trailers, if they are ready. - get: func() -> option>; + /// Returns a pollable which becomes ready when either the trailers have + /// been received, or an error has occured. When this pollable is ready, + /// the `get` method will return `some`. + subscribe: func() -> pollable; + + /// Returns the contents of the trailers, or an error which occured, + /// once the future is ready. + /// + /// The outer `option` represents future readiness. Users can wait on this + /// `option` to become `some` using the `subscribe` method. + /// + /// The outer `result` is used to retrieve the trailers or error at most + /// once. It will be success on the first call in which the outer option + /// is `some`, and error on subsequent calls. + /// + /// The inner `result` represents that either the HTTP Request or Response + /// body, as well as any trailers, were received successfully, or that an + /// error occured receiving them. The optional `trailers` indicates whether + /// or not trailers were present in the body. + /// + /// When some `trailers` are returned by this method, the `trailers` + /// resource is immutable, and a child. Use of the `set`, `append`, or + /// `delete` methods will return an error, and the resource must be + /// dropped before the parent `future-trailers` is dropped. + get: func() -> option, error-code>>>; } + /// Represents an outgoing HTTP Response. resource outgoing-response { - constructor(status-code: status-code, headers: borrow); - /// Will give the child outgoing-response at most once. subsequent calls will - /// return an error. - write: func() -> result; + /// Construct an `outgoing-response`, with a default `status-code` of `200`. + /// If a different `status-code` is needed, it must be set via the + /// `set-status-code` method. + /// + /// * `headers` is the HTTP Headers for the Response. + constructor(headers: headers); + + /// Get the HTTP Status Code for the Response. + status-code: func() -> status-code; + + /// Set the HTTP Status Code for the Response. Fails if the status-code + /// given is not a valid http status code. + set-status-code: func(status-code: status-code) -> result; + + /// Get the headers associated with the Request. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + /// + /// This headers resource is a child: it must be dropped before the parent + /// `outgoing-request` is dropped, or its ownership is transfered to + /// another component by e.g. `outgoing-handler.handle`. + headers: func() -> headers; + + /// Returns the resource corresponding to the outgoing Body for this Response. + /// + /// Returns success on the first call: the `outgoing-body` resource for + /// this `outgoing-response` can be retrieved at most once. Subsequent + /// calls will return error. + body: func() -> result; } + /// Represents an outgoing HTTP Request or Response's Body. + /// + /// A body has both its contents - a stream of bytes - and a (possibly + /// empty) set of trailers, inducating the full contents of the body + /// have been sent. This resource represents the contents as an + /// `output-stream` child resource, and the completion of the body (with + /// optional trailers) with a static function that consumes the + /// `outgoing-body` resource, and ensures that the user of this interface + /// may not write to the body contents after the body has been finished. + /// + /// If the user code drops this resource, as opposed to calling the static + /// method `finish`, the implementation should treat the body as incomplete, + /// and that an error has occured. The implementation should propogate this + /// error to the HTTP protocol by whatever means it has available, + /// including: corrupting the body on the wire, aborting the associated + /// Request, or sending a late status code for the Response. resource outgoing-body { - /// Will give the child output-stream at most once. subsequent calls will - /// return an error. - write: func() -> result; + + /// Returns a stream for writing the body contents. + /// + /// The returned `output-stream` is a child resource: it must be dropped + /// before the parent `outgoing-body` resource is dropped (or finished), + /// otherwise the `outgoing-body` drop or `finish` will trap. + /// + /// Returns success on the first call: the `output-stream` resource for + /// this `outgoing-body` may be retrieved at most once. Subsequent calls + /// will return error. + write: func() -> result; /// Finalize an outgoing body, optionally providing trailers. This must be - /// called to signal that the response is complete. If the `outgoing-body` is - /// dropped without calling `outgoing-body-finalize`, the implementation + /// called to signal that the response is complete. If the `outgoing-body` + /// is dropped without calling `outgoing-body.finalize`, the implementation /// should treat the body as corrupted. - finish: static func(this: outgoing-body, trailers: option); + /// + /// Fails if the body's `outgoing-request` or `outgoing-response` was + /// constructed with a Content-Length header, and the contents written + /// to the body (via `write`) does not match the value given in the + /// Content-Length. + finish: static func( + this: outgoing-body, + trailers: option + ) -> result<_, error-code>; } - /// The following block defines a special resource type used by the - /// `wasi:http/outgoing-handler` interface to emulate - /// `future>` in advance of Preview3. Given a - /// `future-incoming-response`, the client can call the non-blocking `get` - /// method to get the result if it is available. If the result is not available, - /// the client can call `listen` to get a `pollable` that can be passed to - /// `wasi:io/poll.poll-list`. + /// Represents a future which may eventaully return an incoming HTTP + /// Response, or an error. + /// + /// This resource is returned by the `wasi:http/outgoing-handler` interface to + /// provide the HTTP Response corresponding to the sent Request. resource future-incoming-response { - /// option indicates readiness. - /// outer result indicates you are allowed to get the - /// incoming-response-or-error at most once. subsequent calls after ready - /// will return an error here. - /// inner result indicates whether the incoming-response was available, or an - /// error occured. - get: func() -> option>>; - - subscribe: func() -> /* child */ pollable; + /// Returns a pollable which becomes ready when either the Response has + /// been received, or an error has occured. When this pollable is ready, + /// the `get` method will return `some`. + subscribe: func() -> pollable; + + /// Returns the incoming HTTP Response, or an error, once one is ready. + /// + /// The outer `option` represents future readiness. Users can wait on this + /// `option` to become `some` using the `subscribe` method. + /// + /// The outer `result` is used to retrieve the response or error at most + /// once. It will be success on the first call in which the outer option + /// is `some`, and error on subsequent calls. + /// + /// The inner `result` represents that either the incoming HTTP Response + /// status and headers have recieved successfully, or that an error + /// occured. Errors may also occur while consuming the response body, + /// but those will be reported by the `incoming-body` and its + /// `output-stream` child. + get: func() -> option>>; + } } diff --git a/wit/deps/io/error.wit b/wit/deps/io/error.wit index 31918ac..22e5b64 100644 --- a/wit/deps/io/error.wit +++ b/wit/deps/io/error.wit @@ -1,4 +1,4 @@ -package wasi:io@0.2.0-rc-2023-11-10; +package wasi:io@0.2.0; interface error { diff --git a/wit/deps/io/poll.wit b/wit/deps/io/poll.wit index bddde3c..ddc67f8 100644 --- a/wit/deps/io/poll.wit +++ b/wit/deps/io/poll.wit @@ -1,9 +1,9 @@ -package wasi:io@0.2.0-rc-2023-11-10; +package wasi:io@0.2.0; /// A poll API intended to let users wait for I/O events on multiple handles /// at once. interface poll { - /// `pollable` epresents a single I/O event which may be ready, or not. + /// `pollable` represents a single I/O event which may be ready, or not. resource pollable { /// Return the readiness of a pollable. This function never blocks. diff --git a/wit/deps/io/streams.wit b/wit/deps/io/streams.wit index e7e1b68..6d2f871 100644 --- a/wit/deps/io/streams.wit +++ b/wit/deps/io/streams.wit @@ -1,4 +1,4 @@ -package wasi:io@0.2.0-rc-2023-11-10; +package wasi:io@0.2.0; /// WASI I/O is an I/O abstraction API which is currently focused on providing /// stream types. @@ -32,6 +32,11 @@ interface streams { resource input-stream { /// Perform a non-blocking read from the stream. /// + /// When the source of a `read` is binary data, the bytes from the source + /// are returned verbatim. When the source of a `read` is known to the + /// implementation to be text, bytes containing the UTF-8 encoding of the + /// text are returned. + /// /// This function returns a list of bytes containing the read data, /// when successful. The returned list will contain up to `len` bytes; /// it may return fewer than requested, but not more. The list is @@ -111,6 +116,12 @@ interface streams { /// Perform a write. This function never blocks. /// + /// When the destination of a `write` is binary data, the bytes from + /// `contents` are written verbatim. When the destination of a `write` is + /// known to the implementation to be text, the bytes of `contents` are + /// transcoded from UTF-8 into the encoding of the destination and then + /// written. + /// /// Precondition: check-write gave permit of Ok(n) and contents has a /// length of less than or equal to n. Otherwise, this function will trap. /// @@ -131,7 +142,7 @@ interface streams { /// let pollable = this.subscribe(); /// while !contents.is_empty() { /// // Wait for the stream to become writable - /// poll-one(pollable); + /// pollable.block(); /// let Ok(n) = this.check-write(); // eliding error handling /// let len = min(n, contents.len()); /// let (chunk, rest) = contents.split_at(len); @@ -140,7 +151,7 @@ interface streams { /// } /// this.flush(); /// // Wait for completion of `flush` - /// poll-one(pollable); + /// pollable.block(); /// // Check for any errors that arose during `flush` /// let _ = this.check-write(); // eliding error handling /// ``` @@ -178,7 +189,7 @@ interface streams { /// Write zeroes to a stream. /// - /// this should be used precisely like `write` with the exact same + /// This should be used precisely like `write` with the exact same /// preconditions (must use check-write first), but instead of /// passing a list of bytes, you simply pass the number of zero-bytes /// that should be written. @@ -199,7 +210,7 @@ interface streams { /// let pollable = this.subscribe(); /// while num_zeroes != 0 { /// // Wait for the stream to become writable - /// poll-one(pollable); + /// pollable.block(); /// let Ok(n) = this.check-write(); // eliding error handling /// let len = min(n, num_zeroes); /// this.write-zeroes(len); // eliding error handling @@ -207,7 +218,7 @@ interface streams { /// } /// this.flush(); /// // Wait for completion of `flush` - /// poll-one(pollable); + /// pollable.block(); /// // Check for any errors that arose during `flush` /// let _ = this.check-write(); // eliding error handling /// ``` diff --git a/wit/deps/io/world.wit b/wit/deps/io/world.wit index 8243da2..5f0b43f 100644 --- a/wit/deps/io/world.wit +++ b/wit/deps/io/world.wit @@ -1,4 +1,4 @@ -package wasi:io@0.2.0-rc-2023-11-10; +package wasi:io@0.2.0; world imports { import streams; diff --git a/wit/deps/random/insecure-seed.wit b/wit/deps/random/insecure-seed.wit index f76e87d..47210ac 100644 --- a/wit/deps/random/insecure-seed.wit +++ b/wit/deps/random/insecure-seed.wit @@ -1,4 +1,4 @@ -package wasi:random@0.2.0-rc-2023-11-10; +package wasi:random@0.2.0; /// The insecure-seed interface for seeding hash-map DoS resistance. /// /// It is intended to be portable at least between Unix-family platforms and diff --git a/wit/deps/random/insecure.wit b/wit/deps/random/insecure.wit index ec7b997..c58f4ee 100644 --- a/wit/deps/random/insecure.wit +++ b/wit/deps/random/insecure.wit @@ -1,4 +1,4 @@ -package wasi:random@0.2.0-rc-2023-11-10; +package wasi:random@0.2.0; /// The insecure interface for insecure pseudo-random numbers. /// /// It is intended to be portable at least between Unix-family platforms and diff --git a/wit/deps/random/random.wit b/wit/deps/random/random.wit index 7a7dfa2..0c017f0 100644 --- a/wit/deps/random/random.wit +++ b/wit/deps/random/random.wit @@ -1,4 +1,4 @@ -package wasi:random@0.2.0-rc-2023-11-10; +package wasi:random@0.2.0; /// WASI Random is a random data API. /// /// It is intended to be portable at least between Unix-family platforms and diff --git a/wit/deps/random/world.wit b/wit/deps/random/world.wit index 49e5743..3da3491 100644 --- a/wit/deps/random/world.wit +++ b/wit/deps/random/world.wit @@ -1,4 +1,4 @@ -package wasi:random@0.2.0-rc-2023-11-10; +package wasi:random@0.2.0; world imports { import random; diff --git a/wit/deps/sockets/instance-network.wit b/wit/deps/sockets/instance-network.wit index 14e4479..e455d0f 100644 --- a/wit/deps/sockets/instance-network.wit +++ b/wit/deps/sockets/instance-network.wit @@ -1,9 +1,9 @@ /// This interface provides a value-export of the default network handle.. interface instance-network { - use network.{network}; + use network.{network}; - /// Get a handle to the default network. - instance-network: func() -> network; + /// Get a handle to the default network. + instance-network: func() -> network; } diff --git a/wit/deps/sockets/ip-name-lookup.wit b/wit/deps/sockets/ip-name-lookup.wit index 5e8bd27..8e639ec 100644 --- a/wit/deps/sockets/ip-name-lookup.wit +++ b/wit/deps/sockets/ip-name-lookup.wit @@ -1,61 +1,51 @@ interface ip-name-lookup { - use wasi:io/poll@0.2.0-rc-2023-11-10.{pollable}; - use network.{network, error-code, ip-address, ip-address-family}; + use wasi:io/poll@0.2.0.{pollable}; + use network.{network, error-code, ip-address}; - /// Resolve an internet host name to a list of IP addresses. - /// - /// See the wasi-socket proposal README.md for a comparison with getaddrinfo. - /// - /// # Parameters - /// - `name`: The name to look up. IP addresses are not allowed. Unicode domain names are automatically converted - /// to ASCII using IDNA encoding. - /// - `address-family`: If provided, limit the results to addresses of this specific address family. - /// - `include-unavailable`: When set to true, this function will also return addresses of which the runtime - /// thinks (or knows) can't be connected to at the moment. For example, this will return IPv6 addresses on - /// systems without an active IPv6 interface. Notes: - /// - Even when no public IPv6 interfaces are present or active, names like "localhost" can still resolve to an IPv6 address. - /// - Whatever is "available" or "unavailable" is volatile and can change everytime a network cable is unplugged. - /// - /// This function never blocks. It either immediately fails or immediately returns successfully with a `resolve-address-stream` - /// that can be used to (asynchronously) fetch the results. - /// - /// At the moment, the stream never completes successfully with 0 items. Ie. the first call - /// to `resolve-next-address` never returns `ok(none)`. This may change in the future. - /// - /// # Typical errors - /// - `invalid-argument`: `name` is a syntactically invalid domain name. - /// - `invalid-argument`: `name` is an IP address. - /// - `not-supported`: The specified `address-family` is not supported. (EAI_FAMILY) - /// - /// # References: - /// - - /// - - /// - - /// - - resolve-addresses: func(network: borrow, name: string, address-family: option, include-unavailable: bool) -> result; + /// Resolve an internet host name to a list of IP addresses. + /// + /// Unicode domain names are automatically converted to ASCII using IDNA encoding. + /// If the input is an IP address string, the address is parsed and returned + /// as-is without making any external requests. + /// + /// See the wasi-socket proposal README.md for a comparison with getaddrinfo. + /// + /// This function never blocks. It either immediately fails or immediately + /// returns successfully with a `resolve-address-stream` that can be used + /// to (asynchronously) fetch the results. + /// + /// # Typical errors + /// - `invalid-argument`: `name` is a syntactically invalid domain name or IP address. + /// + /// # References: + /// - + /// - + /// - + /// - + resolve-addresses: func(network: borrow, name: string) -> result; - resource resolve-address-stream { - /// Returns the next address from the resolver. - /// - /// This function should be called multiple times. On each call, it will - /// return the next address in connection order preference. If all - /// addresses have been exhausted, this function returns `none`. - /// - /// This function never returns IPv4-mapped IPv6 addresses. - /// - /// # Typical errors - /// - `name-unresolvable`: Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY) - /// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN) - /// - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL) - /// - `would-block`: A result is not available yet. (EWOULDBLOCK, EAGAIN) - resolve-next-address: func() -> result, error-code>; + resource resolve-address-stream { + /// Returns the next address from the resolver. + /// + /// This function should be called multiple times. On each call, it will + /// return the next address in connection order preference. If all + /// addresses have been exhausted, this function returns `none`. + /// + /// This function never returns IPv4-mapped IPv6 addresses. + /// + /// # Typical errors + /// - `name-unresolvable`: Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY) + /// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN) + /// - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL) + /// - `would-block`: A result is not available yet. (EWOULDBLOCK, EAGAIN) + resolve-next-address: func() -> result, error-code>; - /// Create a `pollable` which will resolve once the stream is ready for I/O. - /// - /// Note: this function is here for WASI Preview2 only. - /// It's planned to be removed when `future` is natively supported in Preview3. - subscribe: func() -> pollable; - } + /// Create a `pollable` which will resolve once the stream is ready for I/O. + /// + /// Note: this function is here for WASI Preview2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + subscribe: func() -> pollable; + } } diff --git a/wit/deps/sockets/network.wit b/wit/deps/sockets/network.wit index fc51604..9cadf06 100644 --- a/wit/deps/sockets/network.wit +++ b/wit/deps/sockets/network.wit @@ -1,146 +1,145 @@ interface network { - /// An opaque resource that represents access to (a subset of) the network. - /// This enables context-based security for networking. - /// There is no need for this to map 1:1 to a physical network interface. - resource network; + /// An opaque resource that represents access to (a subset of) the network. + /// This enables context-based security for networking. + /// There is no need for this to map 1:1 to a physical network interface. + resource network; - /// Error codes. - /// - /// In theory, every API can return any error code. - /// In practice, API's typically only return the errors documented per API - /// combined with a couple of errors that are always possible: - /// - `unknown` - /// - `access-denied` - /// - `not-supported` - /// - `out-of-memory` - /// - `concurrency-conflict` - /// - /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. - enum error-code { - // ### GENERAL ERRORS ### + /// Error codes. + /// + /// In theory, every API can return any error code. + /// In practice, API's typically only return the errors documented per API + /// combined with a couple of errors that are always possible: + /// - `unknown` + /// - `access-denied` + /// - `not-supported` + /// - `out-of-memory` + /// - `concurrency-conflict` + /// + /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. + enum error-code { + /// Unknown error + unknown, - /// Unknown error - unknown, + /// Access denied. + /// + /// POSIX equivalent: EACCES, EPERM + access-denied, - /// Access denied. - /// - /// POSIX equivalent: EACCES, EPERM - access-denied, + /// The operation is not supported. + /// + /// POSIX equivalent: EOPNOTSUPP + not-supported, + + /// One of the arguments is invalid. + /// + /// POSIX equivalent: EINVAL + invalid-argument, + + /// Not enough memory to complete the operation. + /// + /// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY + out-of-memory, + + /// The operation timed out before it could finish completely. + timeout, + + /// This operation is incompatible with another asynchronous operation that is already in progress. + /// + /// POSIX equivalent: EALREADY + concurrency-conflict, + + /// Trying to finish an asynchronous operation that: + /// - has not been started yet, or: + /// - was already finished by a previous `finish-*` call. + /// + /// Note: this is scheduled to be removed when `future`s are natively supported. + not-in-progress, + + /// The operation has been aborted because it could not be completed immediately. + /// + /// Note: this is scheduled to be removed when `future`s are natively supported. + would-block, + + + /// The operation is not valid in the socket's current state. + invalid-state, + + /// A new socket resource could not be created because of a system limit. + new-socket-limit, + + /// A bind operation failed because the provided address is not an address that the `network` can bind to. + address-not-bindable, + + /// A bind operation failed because the provided address is already in use or because there are no ephemeral ports available. + address-in-use, + + /// The remote address is not reachable + remote-unreachable, + + + /// The TCP connection was forcefully rejected + connection-refused, + + /// The TCP connection was reset. + connection-reset, + + /// A TCP connection was aborted. + connection-aborted, + + + /// The size of a datagram sent to a UDP socket exceeded the maximum + /// supported size. + datagram-too-large, + + + /// Name does not exist or has no suitable associated IP addresses. + name-unresolvable, + + /// A temporary failure in name resolution occurred. + temporary-resolver-failure, + + /// A permanent failure in name resolution occurred. + permanent-resolver-failure, + } + + enum ip-address-family { + /// Similar to `AF_INET` in POSIX. + ipv4, + + /// Similar to `AF_INET6` in POSIX. + ipv6, + } + + type ipv4-address = tuple; + type ipv6-address = tuple; + + variant ip-address { + ipv4(ipv4-address), + ipv6(ipv6-address), + } - /// The operation is not supported. - /// - /// POSIX equivalent: EOPNOTSUPP - not-supported, + record ipv4-socket-address { + /// sin_port + port: u16, + /// sin_addr + address: ipv4-address, + } - /// One of the arguments is invalid. - /// - /// POSIX equivalent: EINVAL - invalid-argument, + record ipv6-socket-address { + /// sin6_port + port: u16, + /// sin6_flowinfo + flow-info: u32, + /// sin6_addr + address: ipv6-address, + /// sin6_scope_id + scope-id: u32, + } - /// Not enough memory to complete the operation. - /// - /// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY - out-of-memory, - - /// The operation timed out before it could finish completely. - timeout, - - /// This operation is incompatible with another asynchronous operation that is already in progress. - /// - /// POSIX equivalent: EALREADY - concurrency-conflict, - - /// Trying to finish an asynchronous operation that: - /// - has not been started yet, or: - /// - was already finished by a previous `finish-*` call. - /// - /// Note: this is scheduled to be removed when `future`s are natively supported. - not-in-progress, - - /// The operation has been aborted because it could not be completed immediately. - /// - /// Note: this is scheduled to be removed when `future`s are natively supported. - would-block, - - - - // ### TCP & UDP SOCKET ERRORS ### - - /// The operation is not valid in the socket's current state. - invalid-state, - - /// A new socket resource could not be created because of a system limit. - new-socket-limit, - - /// A bind operation failed because the provided address is not an address that the `network` can bind to. - address-not-bindable, - - /// A bind operation failed because the provided address is already in use or because there are no ephemeral ports available. - address-in-use, - - /// The remote address is not reachable - remote-unreachable, - - - // ### TCP SOCKET ERRORS ### - - /// The connection was forcefully rejected - connection-refused, - - /// The connection was reset. - connection-reset, - - /// A connection was aborted. - connection-aborted, - - // ### UDP SOCKET ERRORS ### - datagram-too-large, - - - // ### NAME LOOKUP ERRORS ### - - /// Name does not exist or has no suitable associated IP addresses. - name-unresolvable, - - /// A temporary failure in name resolution occurred. - temporary-resolver-failure, - - /// A permanent failure in name resolution occurred. - permanent-resolver-failure, - } - - enum ip-address-family { - /// Similar to `AF_INET` in POSIX. - ipv4, - - /// Similar to `AF_INET6` in POSIX. - ipv6, - } - - type ipv4-address = tuple; - type ipv6-address = tuple; - - variant ip-address { - ipv4(ipv4-address), - ipv6(ipv6-address), - } - - record ipv4-socket-address { - port: u16, // sin_port - address: ipv4-address, // sin_addr - } - - record ipv6-socket-address { - port: u16, // sin6_port - flow-info: u32, // sin6_flowinfo - address: ipv6-address, // sin6_addr - scope-id: u32, // sin6_scope_id - } - - variant ip-socket-address { - ipv4(ipv4-socket-address), - ipv6(ipv6-socket-address), - } + variant ip-socket-address { + ipv4(ipv4-socket-address), + ipv6(ipv6-socket-address), + } } diff --git a/wit/deps/sockets/tcp-create-socket.wit b/wit/deps/sockets/tcp-create-socket.wit index a9a3373..c7ddf1f 100644 --- a/wit/deps/sockets/tcp-create-socket.wit +++ b/wit/deps/sockets/tcp-create-socket.wit @@ -1,26 +1,27 @@ interface tcp-create-socket { - use network.{network, error-code, ip-address-family}; - use tcp.{tcp-socket}; + use network.{network, error-code, ip-address-family}; + use tcp.{tcp-socket}; - /// Create a new TCP socket. - /// - /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. - /// - /// This function does not require a network capability handle. This is considered to be safe because - /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`listen`/`connect` - /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world. - /// - /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. - /// - /// # Typical errors - /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) - /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) - /// - /// # References - /// - - /// - - /// - - /// - - create-tcp-socket: func(address-family: ip-address-family) -> result; + /// Create a new TCP socket. + /// + /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. + /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. + /// + /// This function does not require a network capability handle. This is considered to be safe because + /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` + /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world. + /// + /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. + /// + /// # Typical errors + /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// + /// # References + /// - + /// - + /// - + /// - + create-tcp-socket: func(address-family: ip-address-family) -> result; } diff --git a/wit/deps/sockets/tcp.wit b/wit/deps/sockets/tcp.wit index 76caf88..5902b9e 100644 --- a/wit/deps/sockets/tcp.wit +++ b/wit/deps/sockets/tcp.wit @@ -1,268 +1,353 @@ interface tcp { - use wasi:io/streams@0.2.0-rc-2023-11-10.{input-stream, output-stream}; - use wasi:io/poll@0.2.0-rc-2023-11-10.{pollable}; - use network.{network, error-code, ip-socket-address, ip-address-family}; + use wasi:io/streams@0.2.0.{input-stream, output-stream}; + use wasi:io/poll@0.2.0.{pollable}; + use wasi:clocks/monotonic-clock@0.2.0.{duration}; + use network.{network, error-code, ip-socket-address, ip-address-family}; - enum shutdown-type { - /// Similar to `SHUT_RD` in POSIX. - receive, + enum shutdown-type { + /// Similar to `SHUT_RD` in POSIX. + receive, - /// Similar to `SHUT_WR` in POSIX. - send, + /// Similar to `SHUT_WR` in POSIX. + send, - /// Similar to `SHUT_RDWR` in POSIX. - both, - } + /// Similar to `SHUT_RDWR` in POSIX. + both, + } + + /// A TCP socket resource. + /// + /// The socket can be in one of the following states: + /// - `unbound` + /// - `bind-in-progress` + /// - `bound` (See note below) + /// - `listen-in-progress` + /// - `listening` + /// - `connect-in-progress` + /// - `connected` + /// - `closed` + /// See + /// for a more information. + /// + /// Note: Except where explicitly mentioned, whenever this documentation uses + /// the term "bound" without backticks it actually means: in the `bound` state *or higher*. + /// (i.e. `bound`, `listen-in-progress`, `listening`, `connect-in-progress` or `connected`) + /// + /// In addition to the general error codes documented on the + /// `network::error-code` type, TCP socket methods may always return + /// `error(invalid-state)` when in the `closed` state. + resource tcp-socket { + /// Bind the socket to a specific network on the provided IP address and port. + /// + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which + /// network interface(s) to bind to. + /// If the TCP/UDP port is zero, the socket will be bound to a random free port. + /// + /// Bind can be attempted multiple times on the same socket, even with + /// different arguments on each iteration. But never concurrently and + /// only as long as the previous bind failed. Once a bind succeeds, the + /// binding can't be changed anymore. + /// + /// # Typical errors + /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) + /// - `invalid-argument`: `local-address` is not a unicast address. (EINVAL) + /// - `invalid-argument`: `local-address` is an IPv4-mapped IPv6 address. (EINVAL) + /// - `invalid-state`: The socket is already bound. (EINVAL) + /// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: Address is already in use. (EADDRINUSE) + /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) + /// - `not-in-progress`: A `bind` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # Implementors note + /// When binding to a non-zero port, this bind operation shouldn't be affected by the TIME_WAIT + /// state of a recently closed socket on the same local address. In practice this means that the SO_REUSEADDR + /// socket option should be set implicitly on all platforms, except on Windows where this is the default behavior + /// and SO_REUSEADDR performs something different entirely. + /// + /// Unlike in POSIX, in WASI the bind operation is async. This enables + /// interactive WASI hosts to inject permission prompts. Runtimes that + /// don't want to make use of this ability can simply call the native + /// `bind` as part of either `start-bind` or `finish-bind`. + /// + /// # References + /// - + /// - + /// - + /// - + start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; + finish-bind: func() -> result<_, error-code>; + /// Connect to a remote endpoint. + /// + /// On success: + /// - the socket is transitioned into the `connection` state. + /// - a pair of streams is returned that can be used to read & write to the connection + /// + /// After a failed connection attempt, the socket will be in the `closed` + /// state and the only valid action left is to `drop` the socket. A single + /// socket can not be used to connect more than once. + /// + /// # Typical errors + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: `remote-address` is not a unicast address. (EINVAL, ENETUNREACH on Linux, EAFNOSUPPORT on MacOS) + /// - `invalid-argument`: `remote-address` is an IPv4-mapped IPv6 address. (EINVAL, EADDRNOTAVAIL on Illumos) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows) + /// - `invalid-argument`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. + /// - `invalid-state`: The socket is already in the `connected` state. (EISCONN) + /// - `invalid-state`: The socket is already in the `listening` state. (EOPNOTSUPP, EINVAL on Windows) + /// - `timeout`: Connection timed out. (ETIMEDOUT) + /// - `connection-refused`: The connection was forcefully rejected. (ECONNREFUSED) + /// - `connection-reset`: The connection was reset. (ECONNRESET) + /// - `connection-aborted`: The connection was aborted. (ECONNABORTED) + /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// - `not-in-progress`: A connect operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # Implementors note + /// The POSIX equivalent of `start-connect` is the regular `connect` syscall. + /// Because all WASI sockets are non-blocking this is expected to return + /// EINPROGRESS, which should be translated to `ok()` in WASI. + /// + /// The POSIX equivalent of `finish-connect` is a `poll` for event `POLLOUT` + /// with a timeout of 0 on the socket descriptor. Followed by a check for + /// the `SO_ERROR` socket option, in case the poll signaled readiness. + /// + /// # References + /// - + /// - + /// - + /// - + start-connect: func(network: borrow, remote-address: ip-socket-address) -> result<_, error-code>; + finish-connect: func() -> result, error-code>; - /// A TCP socket handle. - resource tcp-socket { - /// Bind the socket to a specific network on the provided IP address and port. - /// - /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which - /// network interface(s) to bind to. - /// If the TCP/UDP port is zero, the socket will be bound to a random free port. - /// - /// When a socket is not explicitly bound, the first invocation to a listen or connect operation will - /// implicitly bind the socket. - /// - /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. - /// - /// # Typical `start` errors - /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) - /// - `invalid-argument`: `local-address` is not a unicast address. (EINVAL) - /// - `invalid-argument`: `local-address` is an IPv4-mapped IPv6 address, but the socket has `ipv6-only` enabled. (EINVAL) - /// - `invalid-state`: The socket is already bound. (EINVAL) - /// - /// # Typical `finish` errors - /// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) - /// - `address-in-use`: Address is already in use. (EADDRINUSE) - /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) - /// - `not-in-progress`: A `bind` operation is not in progress. - /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) - /// - /// # References - /// - - /// - - /// - - /// - - start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; - finish-bind: func() -> result<_, error-code>; + /// Start listening for new connections. + /// + /// Transitions the socket into the `listening` state. + /// + /// Unlike POSIX, the socket must already be explicitly bound. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not bound to any local address. (EDESTADDRREQ) + /// - `invalid-state`: The socket is already in the `connected` state. (EISCONN, EINVAL on BSD) + /// - `invalid-state`: The socket is already in the `listening` state. + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) + /// - `not-in-progress`: A listen operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # Implementors note + /// Unlike in POSIX, in WASI the listen operation is async. This enables + /// interactive WASI hosts to inject permission prompts. Runtimes that + /// don't want to make use of this ability can simply call the native + /// `listen` as part of either `start-listen` or `finish-listen`. + /// + /// # References + /// - + /// - + /// - + /// - + start-listen: func() -> result<_, error-code>; + finish-listen: func() -> result<_, error-code>; - /// Connect to a remote endpoint. - /// - /// On success: - /// - the socket is transitioned into the Connection state - /// - a pair of streams is returned that can be used to read & write to the connection - /// - /// POSIX mentions: - /// > If connect() fails, the state of the socket is unspecified. Conforming applications should - /// > close the file descriptor and create a new socket before attempting to reconnect. - /// - /// WASI prescribes the following behavior: - /// - If `connect` fails because an input/state validation error, the socket should remain usable. - /// - If a connection was actually attempted but failed, the socket should become unusable for further network communication. - /// Besides `drop`, any method after such a failure may return an error. - /// - /// # Typical `start` errors - /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) - /// - `invalid-argument`: `remote-address` is not a unicast address. (EINVAL, ENETUNREACH on Linux, EAFNOSUPPORT on MacOS) - /// - `invalid-argument`: `remote-address` is an IPv4-mapped IPv6 address, but the socket has `ipv6-only` enabled. (EINVAL, EADDRNOTAVAIL on Illumos) - /// - `invalid-argument`: `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa) - /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) - /// - `invalid-argument`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows) - /// - `invalid-argument`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. - /// - `invalid-state`: The socket is already in the Connection state. (EISCONN) - /// - `invalid-state`: The socket is already in the Listener state. (EOPNOTSUPP, EINVAL on Windows) - /// - /// # Typical `finish` errors - /// - `timeout`: Connection timed out. (ETIMEDOUT) - /// - `connection-refused`: The connection was forcefully rejected. (ECONNREFUSED) - /// - `connection-reset`: The connection was reset. (ECONNRESET) - /// - `connection-aborted`: The connection was aborted. (ECONNABORTED) - /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) - /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) - /// - `not-in-progress`: A `connect` operation is not in progress. - /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) - /// - /// # References - /// - - /// - - /// - - /// - - start-connect: func(network: borrow, remote-address: ip-socket-address) -> result<_, error-code>; - finish-connect: func() -> result, error-code>; + /// Accept a new client socket. + /// + /// The returned socket is bound and in the `connected` state. The following properties are inherited from the listener socket: + /// - `address-family` + /// - `keep-alive-enabled` + /// - `keep-alive-idle-time` + /// - `keep-alive-interval` + /// - `keep-alive-count` + /// - `hop-limit` + /// - `receive-buffer-size` + /// - `send-buffer-size` + /// + /// On success, this function returns the newly accepted client socket along with + /// a pair of streams that can be used to read & write to the connection. + /// + /// # Typical errors + /// - `invalid-state`: Socket is not in the `listening` state. (EINVAL) + /// - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN) + /// - `connection-aborted`: An incoming connection was pending, but was terminated by the client before this listener could accept it. (ECONNABORTED) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// + /// # References + /// - + /// - + /// - + /// - + accept: func() -> result, error-code>; - /// Start listening for new connections. - /// - /// Transitions the socket into the Listener state. - /// - /// Unlike POSIX: - /// - this function is async. This enables interactive WASI hosts to inject permission prompts. - /// - the socket must already be explicitly bound. - /// - /// # Typical `start` errors - /// - `invalid-state`: The socket is not bound to any local address. (EDESTADDRREQ) - /// - `invalid-state`: The socket is already in the Connection state. (EISCONN, EINVAL on BSD) - /// - `invalid-state`: The socket is already in the Listener state. - /// - /// # Typical `finish` errors - /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) - /// - `not-in-progress`: A `listen` operation is not in progress. - /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) - /// - /// # References - /// - - /// - - /// - - /// - - start-listen: func() -> result<_, error-code>; - finish-listen: func() -> result<_, error-code>; + /// Get the bound local address. + /// + /// POSIX mentions: + /// > If the socket has not been bound to a local name, the value + /// > stored in the object pointed to by `address` is unspecified. + /// + /// WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not bound to any local address. + /// + /// # References + /// - + /// - + /// - + /// - + local-address: func() -> result; - /// Accept a new client socket. - /// - /// The returned socket is bound and in the Connection state. The following properties are inherited from the listener socket: - /// - `address-family` - /// - `ipv6-only` - /// - `keep-alive` - /// - `no-delay` - /// - `unicast-hop-limit` - /// - `receive-buffer-size` - /// - `send-buffer-size` - /// - /// On success, this function returns the newly accepted client socket along with - /// a pair of streams that can be used to read & write to the connection. - /// - /// # Typical errors - /// - `invalid-state`: Socket is not in the Listener state. (EINVAL) - /// - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN) - /// - `connection-aborted`: An incoming connection was pending, but was terminated by the client before this listener could accept it. (ECONNABORTED) - /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) - /// - /// # References - /// - - /// - - /// - - /// - - accept: func() -> result, error-code>; + /// Get the remote address. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + remote-address: func() -> result; - /// Get the bound local address. - /// - /// POSIX mentions: - /// > If the socket has not been bound to a local name, the value - /// > stored in the object pointed to by `address` is unspecified. - /// - /// WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet. - /// - /// # Typical errors - /// - `invalid-state`: The socket is not bound to any local address. - /// - /// # References - /// - - /// - - /// - - /// - - local-address: func() -> result; + /// Whether the socket is in the `listening` state. + /// + /// Equivalent to the SO_ACCEPTCONN socket option. + is-listening: func() -> bool; - /// Get the remote address. - /// - /// # Typical errors - /// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN) - /// - /// # References - /// - - /// - - /// - - /// - - remote-address: func() -> result; + /// Whether this is a IPv4 or IPv6 socket. + /// + /// Equivalent to the SO_DOMAIN socket option. + address-family: func() -> ip-address-family; - /// Whether this is a IPv4 or IPv6 socket. - /// - /// Equivalent to the SO_DOMAIN socket option. - address-family: func() -> ip-address-family; + /// Hints the desired listen queue size. Implementations are free to ignore this. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// + /// # Typical errors + /// - `not-supported`: (set) The platform does not support changing the backlog size after the initial listen. + /// - `invalid-argument`: (set) The provided value was 0. + /// - `invalid-state`: (set) The socket is in the `connect-in-progress` or `connected` state. + set-listen-backlog-size: func(value: u64) -> result<_, error-code>; - /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. - /// - /// Equivalent to the IPV6_V6ONLY socket option. - /// - /// # Typical errors - /// - `invalid-state`: (set) The socket is already bound. - /// - `not-supported`: (get/set) `this` socket is an IPv4 socket. - /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) - ipv6-only: func() -> result; - set-ipv6-only: func(value: bool) -> result<_, error-code>; + /// Enables or disables keepalive. + /// + /// The keepalive behavior can be adjusted using: + /// - `keep-alive-idle-time` + /// - `keep-alive-interval` + /// - `keep-alive-count` + /// These properties can be configured while `keep-alive-enabled` is false, but only come into effect when `keep-alive-enabled` is true. + /// + /// Equivalent to the SO_KEEPALIVE socket option. + keep-alive-enabled: func() -> result; + set-keep-alive-enabled: func(value: bool) -> result<_, error-code>; - /// Hints the desired listen queue size. Implementations are free to ignore this. - /// - /// # Typical errors - /// - `not-supported`: (set) The platform does not support changing the backlog size after the initial listen. - /// - `invalid-state`: (set) The socket is already in the Connection state. - set-listen-backlog-size: func(value: u64) -> result<_, error-code>; + /// Amount of time the connection has to be idle before TCP starts sending keepalive packets. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the TCP_KEEPIDLE socket option. (TCP_KEEPALIVE on MacOS) + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + keep-alive-idle-time: func() -> result; + set-keep-alive-idle-time: func(value: duration) -> result<_, error-code>; - /// Equivalent to the SO_KEEPALIVE socket option. - keep-alive: func() -> result; - set-keep-alive: func(value: bool) -> result<_, error-code>; + /// The time between keepalive packets. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the TCP_KEEPINTVL socket option. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + keep-alive-interval: func() -> result; + set-keep-alive-interval: func(value: duration) -> result<_, error-code>; - /// Equivalent to the TCP_NODELAY socket option. - /// - /// The default value is `false`. - no-delay: func() -> result; - set-no-delay: func(value: bool) -> result<_, error-code>; + /// The maximum amount of keepalive packets TCP should send before aborting the connection. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the TCP_KEEPCNT socket option. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + keep-alive-count: func() -> result; + set-keep-alive-count: func(value: u32) -> result<_, error-code>; - /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. - /// - /// # Typical errors - /// - `invalid-argument`: (set) The TTL value must be 1 or higher. - /// - `invalid-state`: (set) The socket is already in the Connection state. - /// - `invalid-state`: (set) The socket is already in the Listener state. - unicast-hop-limit: func() -> result; - set-unicast-hop-limit: func(value: u8) -> result<_, error-code>; + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The TTL value must be 1 or higher. + hop-limit: func() -> result; + set-hop-limit: func(value: u8) -> result<_, error-code>; - /// The kernel buffer space reserved for sends/receives on this socket. - /// - /// Note #1: an implementation may choose to cap or round the buffer size when setting the value. - /// In other words, after setting a value, reading the same setting back may return a different value. - /// - /// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of - /// actual data to be sent/received by the application, because the kernel might also use the buffer space - /// for internal metadata structures. - /// - /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. - /// - /// # Typical errors - /// - `invalid-state`: (set) The socket is already in the Connection state. - /// - `invalid-state`: (set) The socket is already in the Listener state. - receive-buffer-size: func() -> result; - set-receive-buffer-size: func(value: u64) -> result<_, error-code>; - send-buffer-size: func() -> result; - set-send-buffer-size: func(value: u64) -> result<_, error-code>; + /// The kernel buffer space reserved for sends/receives on this socket. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + receive-buffer-size: func() -> result; + set-receive-buffer-size: func(value: u64) -> result<_, error-code>; + send-buffer-size: func() -> result; + set-send-buffer-size: func(value: u64) -> result<_, error-code>; - /// Create a `pollable` which will resolve once the socket is ready for I/O. - /// - /// Note: this function is here for WASI Preview2 only. - /// It's planned to be removed when `future` is natively supported in Preview3. - subscribe: func() -> pollable; + /// Create a `pollable` which can be used to poll for, or block on, + /// completion of any of the asynchronous operations of this socket. + /// + /// When `finish-bind`, `finish-listen`, `finish-connect` or `accept` + /// return `error(would-block)`, this pollable can be used to wait for + /// their success or failure, after which the method can be retried. + /// + /// The pollable is not limited to the async operation that happens to be + /// in progress at the time of calling `subscribe` (if any). Theoretically, + /// `subscribe` only has to be called once per socket and can then be + /// (re)used for the remainder of the socket's lifetime. + /// + /// See + /// for a more information. + /// + /// Note: this function is here for WASI Preview2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + subscribe: func() -> pollable; - /// Initiate a graceful shutdown. - /// - /// - receive: the socket is not expecting to receive any more data from the peer. All subsequent read - /// operations on the `input-stream` associated with this socket will return an End Of Stream indication. - /// Any data still in the receive queue at time of calling `shutdown` will be discarded. - /// - send: the socket is not expecting to send any more data to the peer. All subsequent write - /// operations on the `output-stream` associated with this socket will return an error. - /// - both: same effect as receive & send combined. - /// - /// The shutdown function does not close (drop) the socket. - /// - /// # Typical errors - /// - `invalid-state`: The socket is not in the Connection state. (ENOTCONN) - /// - /// # References - /// - - /// - - /// - - /// - - shutdown: func(shutdown-type: shutdown-type) -> result<_, error-code>; - } + /// Initiate a graceful shutdown. + /// + /// - `receive`: The socket is not expecting to receive any data from + /// the peer. The `input-stream` associated with this socket will be + /// closed. Any data still in the receive queue at time of calling + /// this method will be discarded. + /// - `send`: The socket has no more data to send to the peer. The `output-stream` + /// associated with this socket will be closed and a FIN packet will be sent. + /// - `both`: Same effect as `receive` & `send` combined. + /// + /// This function is idempotent. Shutting a down a direction more than once + /// has no effect and returns `ok`. + /// + /// The shutdown function does not close (drop) the socket. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not in the `connected` state. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + shutdown: func(shutdown-type: shutdown-type) -> result<_, error-code>; + } } diff --git a/wit/deps/sockets/udp-create-socket.wit b/wit/deps/sockets/udp-create-socket.wit index e026359..0482d1f 100644 --- a/wit/deps/sockets/udp-create-socket.wit +++ b/wit/deps/sockets/udp-create-socket.wit @@ -1,26 +1,27 @@ interface udp-create-socket { - use network.{network, error-code, ip-address-family}; - use udp.{udp-socket}; + use network.{network, error-code, ip-address-family}; + use udp.{udp-socket}; - /// Create a new UDP socket. - /// - /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. - /// - /// This function does not require a network capability handle. This is considered to be safe because - /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` is called, - /// the socket is effectively an in-memory configuration object, unable to communicate with the outside world. - /// - /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. - /// - /// # Typical errors - /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) - /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) - /// - /// # References: - /// - - /// - - /// - - /// - - create-udp-socket: func(address-family: ip-address-family) -> result; + /// Create a new UDP socket. + /// + /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. + /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. + /// + /// This function does not require a network capability handle. This is considered to be safe because + /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind` is called, + /// the socket is effectively an in-memory configuration object, unable to communicate with the outside world. + /// + /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. + /// + /// # Typical errors + /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// + /// # References: + /// - + /// - + /// - + /// - + create-udp-socket: func(address-family: ip-address-family) -> result; } diff --git a/wit/deps/sockets/udp.wit b/wit/deps/sockets/udp.wit index 4c1a9f2..d987a0a 100644 --- a/wit/deps/sockets/udp.wit +++ b/wit/deps/sockets/udp.wit @@ -1,213 +1,266 @@ interface udp { - use wasi:io/poll@0.2.0-rc-2023-11-10.{pollable}; - use network.{network, error-code, ip-socket-address, ip-address-family}; - - - record datagram { - data: list, // Theoretical max size: ~64 KiB. In practice, typically less than 1500 bytes. - remote-address: ip-socket-address, - - /// Possible future additions: - /// local-address: ip-socket-address, // IP_PKTINFO / IP_RECVDSTADDR / IPV6_PKTINFO - /// local-interface: u32, // IP_PKTINFO / IP_RECVIF - /// ttl: u8, // IP_RECVTTL - /// dscp: u6, // IP_RECVTOS - /// ecn: u2, // IP_RECVTOS - } - - - - /// A UDP socket handle. - resource udp-socket { - /// Bind the socket to a specific network on the provided IP address and port. - /// - /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which - /// network interface(s) to bind to. - /// If the TCP/UDP port is zero, the socket will be bound to a random free port. - /// - /// When a socket is not explicitly bound, the first invocation to connect will implicitly bind the socket. - /// - /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. - /// - /// # Typical `start` errors - /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) - /// - `invalid-state`: The socket is already bound. (EINVAL) - /// - /// # Typical `finish` errors - /// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) - /// - `address-in-use`: Address is already in use. (EADDRINUSE) - /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) - /// - `not-in-progress`: A `bind` operation is not in progress. - /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) - /// - /// # References - /// - - /// - - /// - - /// - - start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; - finish-bind: func() -> result<_, error-code>; - - /// Set the destination address. - /// - /// The local-address is updated based on the best network path to `remote-address`. - /// - /// When a destination address is set: - /// - all receive operations will only return datagrams sent from the provided `remote-address`. - /// - the `send` function can only be used to send to this destination. - /// - /// Note that this function does not generate any network traffic and the peer is not aware of this "connection". - /// - /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. - /// - /// # Typical `start` errors - /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) - /// - `invalid-argument`: `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa) - /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) - /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) - /// - `invalid-argument`: The socket is already bound to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. - /// - /// # Typical `finish` errors - /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) - /// - `not-in-progress`: A `connect` operation is not in progress. - /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) - /// - /// # References - /// - - /// - - /// - - /// - - start-connect: func(network: borrow, remote-address: ip-socket-address) -> result<_, error-code>; - finish-connect: func() -> result<_, error-code>; - - /// Receive messages on the socket. - /// - /// This function attempts to receive up to `max-results` datagrams on the socket without blocking. - /// The returned list may contain fewer elements than requested, but never more. - /// If `max-results` is 0, this function returns successfully with an empty list. - /// - /// # Typical errors - /// - `invalid-state`: The socket is not bound to any local address. (EINVAL) - /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) - /// - `would-block`: There is no pending data available to be read at the moment. (EWOULDBLOCK, EAGAIN) - /// - /// # References - /// - - /// - - /// - - /// - - /// - - /// - - /// - - /// - - receive: func(max-results: u64) -> result, error-code>; - - /// Send messages on the socket. - /// - /// This function attempts to send all provided `datagrams` on the socket without blocking and - /// returns how many messages were actually sent (or queued for sending). - /// - /// This function semantically behaves the same as iterating the `datagrams` list and sequentially - /// sending each individual datagram until either the end of the list has been reached or the first error occurred. - /// If at least one datagram has been sent successfully, this function never returns an error. - /// - /// If the input list is empty, the function returns `ok(0)`. - /// - /// The remote address option is required. To send a message to the "connected" peer, - /// call `remote-address` to get their address. - /// - /// # Typical errors - /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) - /// - `invalid-argument`: `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa) - /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) - /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) - /// - `invalid-argument`: The socket is in "connected" mode and the `datagram.remote-address` does not match the address passed to `connect`. (EISCONN) - /// - `invalid-state`: The socket is not bound to any local address. Unlike POSIX, this function does not perform an implicit bind. - /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) - /// - `datagram-too-large`: The datagram is too large. (EMSGSIZE) - /// - `would-block`: The send buffer is currently full. (EWOULDBLOCK, EAGAIN) - /// - /// # References - /// - - /// - - /// - - /// - - /// - - /// - - /// - - /// - - send: func(datagrams: list) -> result; - - /// Get the current bound address. - /// - /// POSIX mentions: - /// > If the socket has not been bound to a local name, the value - /// > stored in the object pointed to by `address` is unspecified. - /// - /// WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet. - /// - /// # Typical errors - /// - `invalid-state`: The socket is not bound to any local address. - /// - /// # References - /// - - /// - - /// - - /// - - local-address: func() -> result; - - /// Get the address set with `connect`. - /// - /// # Typical errors - /// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN) - /// - /// # References - /// - - /// - - /// - - /// - - remote-address: func() -> result; - - /// Whether this is a IPv4 or IPv6 socket. - /// - /// Equivalent to the SO_DOMAIN socket option. - address-family: func() -> ip-address-family; - - /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. - /// - /// Equivalent to the IPV6_V6ONLY socket option. - /// - /// # Typical errors - /// - `not-supported`: (get/set) `this` socket is an IPv4 socket. - /// - `invalid-state`: (set) The socket is already bound. - /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) - ipv6-only: func() -> result; - set-ipv6-only: func(value: bool) -> result<_, error-code>; - - /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. - unicast-hop-limit: func() -> result; - set-unicast-hop-limit: func(value: u8) -> result<_, error-code>; - - /// The kernel buffer space reserved for sends/receives on this socket. - /// - /// Note #1: an implementation may choose to cap or round the buffer size when setting the value. - /// In other words, after setting a value, reading the same setting back may return a different value. - /// - /// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of - /// actual data to be sent/received by the application, because the kernel might also use the buffer space - /// for internal metadata structures. - /// - /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. - receive-buffer-size: func() -> result; - set-receive-buffer-size: func(value: u64) -> result<_, error-code>; - send-buffer-size: func() -> result; - set-send-buffer-size: func(value: u64) -> result<_, error-code>; - - /// Create a `pollable` which will resolve once the socket is ready for I/O. - /// - /// Note: this function is here for WASI Preview2 only. - /// It's planned to be removed when `future` is natively supported in Preview3. - subscribe: func() -> pollable; - } + use wasi:io/poll@0.2.0.{pollable}; + use network.{network, error-code, ip-socket-address, ip-address-family}; + + /// A received datagram. + record incoming-datagram { + /// The payload. + /// + /// Theoretical max size: ~64 KiB. In practice, typically less than 1500 bytes. + data: list, + + /// The source address. + /// + /// This field is guaranteed to match the remote address the stream was initialized with, if any. + /// + /// Equivalent to the `src_addr` out parameter of `recvfrom`. + remote-address: ip-socket-address, + } + + /// A datagram to be sent out. + record outgoing-datagram { + /// The payload. + data: list, + + /// The destination address. + /// + /// The requirements on this field depend on how the stream was initialized: + /// - with a remote address: this field must be None or match the stream's remote address exactly. + /// - without a remote address: this field is required. + /// + /// If this value is None, the send operation is equivalent to `send` in POSIX. Otherwise it is equivalent to `sendto`. + remote-address: option, + } + + + + /// A UDP socket handle. + resource udp-socket { + /// Bind the socket to a specific network on the provided IP address and port. + /// + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which + /// network interface(s) to bind to. + /// If the port is zero, the socket will be bound to a random free port. + /// + /// # Typical errors + /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) + /// - `invalid-state`: The socket is already bound. (EINVAL) + /// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: Address is already in use. (EADDRINUSE) + /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) + /// - `not-in-progress`: A `bind` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # Implementors note + /// Unlike in POSIX, in WASI the bind operation is async. This enables + /// interactive WASI hosts to inject permission prompts. Runtimes that + /// don't want to make use of this ability can simply call the native + /// `bind` as part of either `start-bind` or `finish-bind`. + /// + /// # References + /// - + /// - + /// - + /// - + start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; + finish-bind: func() -> result<_, error-code>; + + /// Set up inbound & outbound communication channels, optionally to a specific peer. + /// + /// This function only changes the local socket configuration and does not generate any network traffic. + /// On success, the `remote-address` of the socket is updated. The `local-address` may be updated as well, + /// based on the best network path to `remote-address`. + /// + /// When a `remote-address` is provided, the returned streams are limited to communicating with that specific peer: + /// - `send` can only be used to send to this destination. + /// - `receive` will only return datagrams sent from the provided `remote-address`. + /// + /// This method may be called multiple times on the same socket to change its association, but + /// only the most recently returned pair of streams will be operational. Implementations may trap if + /// the streams returned by a previous invocation haven't been dropped yet before calling `stream` again. + /// + /// The POSIX equivalent in pseudo-code is: + /// ```text + /// if (was previously connected) { + /// connect(s, AF_UNSPEC) + /// } + /// if (remote_address is Some) { + /// connect(s, remote_address) + /// } + /// ``` + /// + /// Unlike in POSIX, the socket must already be explicitly bound. + /// + /// # Typical errors + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-state`: The socket is not bound. + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `connection-refused`: The connection was refused. (ECONNREFUSED) + /// + /// # References + /// - + /// - + /// - + /// - + %stream: func(remote-address: option) -> result, error-code>; + + /// Get the current bound address. + /// + /// POSIX mentions: + /// > If the socket has not been bound to a local name, the value + /// > stored in the object pointed to by `address` is unspecified. + /// + /// WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not bound to any local address. + /// + /// # References + /// - + /// - + /// - + /// - + local-address: func() -> result; + + /// Get the address the socket is currently streaming to. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not streaming to a specific remote address. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + remote-address: func() -> result; + + /// Whether this is a IPv4 or IPv6 socket. + /// + /// Equivalent to the SO_DOMAIN socket option. + address-family: func() -> ip-address-family; + + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The TTL value must be 1 or higher. + unicast-hop-limit: func() -> result; + set-unicast-hop-limit: func(value: u8) -> result<_, error-code>; + + /// The kernel buffer space reserved for sends/receives on this socket. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + receive-buffer-size: func() -> result; + set-receive-buffer-size: func(value: u64) -> result<_, error-code>; + send-buffer-size: func() -> result; + set-send-buffer-size: func(value: u64) -> result<_, error-code>; + + /// Create a `pollable` which will resolve once the socket is ready for I/O. + /// + /// Note: this function is here for WASI Preview2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + subscribe: func() -> pollable; + } + + resource incoming-datagram-stream { + /// Receive messages on the socket. + /// + /// This function attempts to receive up to `max-results` datagrams on the socket without blocking. + /// The returned list may contain fewer elements than requested, but never more. + /// + /// This function returns successfully with an empty list when either: + /// - `max-results` is 0, or: + /// - `max-results` is greater than 0, but no results are immediately available. + /// This function never returns `error(would-block)`. + /// + /// # Typical errors + /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `connection-refused`: The connection was refused. (ECONNREFUSED) + /// + /// # References + /// - + /// - + /// - + /// - + /// - + /// - + /// - + /// - + receive: func(max-results: u64) -> result, error-code>; + + /// Create a `pollable` which will resolve once the stream is ready to receive again. + /// + /// Note: this function is here for WASI Preview2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + subscribe: func() -> pollable; + } + + resource outgoing-datagram-stream { + /// Check readiness for sending. This function never blocks. + /// + /// Returns the number of datagrams permitted for the next call to `send`, + /// or an error. Calling `send` with more datagrams than this function has + /// permitted will trap. + /// + /// When this function returns ok(0), the `subscribe` pollable will + /// become ready when this function will report at least ok(1), or an + /// error. + /// + /// Never returns `would-block`. + check-send: func() -> result; + + /// Send messages on the socket. + /// + /// This function attempts to send all provided `datagrams` on the socket without blocking and + /// returns how many messages were actually sent (or queued for sending). This function never + /// returns `error(would-block)`. If none of the datagrams were able to be sent, `ok(0)` is returned. + /// + /// This function semantically behaves the same as iterating the `datagrams` list and sequentially + /// sending each individual datagram until either the end of the list has been reached or the first error occurred. + /// If at least one datagram has been sent successfully, this function never returns an error. + /// + /// If the input list is empty, the function returns `ok(0)`. + /// + /// Each call to `send` must be permitted by a preceding `check-send`. Implementations must trap if + /// either `check-send` was not called or `datagrams` contains more items than `check-send` permitted. + /// + /// # Typical errors + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The socket is in "connected" mode and `remote-address` is `some` value that does not match the address passed to `stream`. (EISCONN) + /// - `invalid-argument`: The socket is not "connected" and no value for `remote-address` was provided. (EDESTADDRREQ) + /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `connection-refused`: The connection was refused. (ECONNREFUSED) + /// - `datagram-too-large`: The datagram is too large. (EMSGSIZE) + /// + /// # References + /// - + /// - + /// - + /// - + /// - + /// - + /// - + /// - + send: func(datagrams: list) -> result; + + /// Create a `pollable` which will resolve once the stream is ready to send again. + /// + /// Note: this function is here for WASI Preview2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + subscribe: func() -> pollable; + } } diff --git a/wit/deps/sockets/world.wit b/wit/deps/sockets/world.wit index d16530c..f8bb92a 100644 --- a/wit/deps/sockets/world.wit +++ b/wit/deps/sockets/world.wit @@ -1,4 +1,4 @@ -package wasi:sockets@0.2.0-rc-2023-10-18; +package wasi:sockets@0.2.0; world imports { import instance-network; diff --git a/wit/virt.wit b/wit/virt.wit index 64cbca0..7ed1cfb 100644 --- a/wit/virt.wit +++ b/wit/virt.wit @@ -3,190 +3,190 @@ package local:virt; // in future this should be defined as a union world of the various // virtual subsystems, when union syntax lands world virtual-adapter { - import wasi:cli/environment@0.2.0-rc-2023-12-05; - import wasi:filesystem/preopens@0.2.0-rc-2023-11-10; - import wasi:filesystem/types@0.2.0-rc-2023-11-10; - import wasi:io/error@0.2.0-rc-2023-11-10; - export wasi:io/error@0.2.0-rc-2023-11-10; - import wasi:io/streams@0.2.0-rc-2023-11-10; - export wasi:io/streams@0.2.0-rc-2023-11-10; - import wasi:io/poll@0.2.0-rc-2023-11-10; - export wasi:io/poll@0.2.0-rc-2023-11-10; - export wasi:cli/environment@0.2.0-rc-2023-12-05; - export wasi:filesystem/types@0.2.0-rc-2023-11-10; - export wasi:filesystem/preopens@0.2.0-rc-2023-11-10; - import wasi:cli/stdin@0.2.0-rc-2023-12-05; - import wasi:cli/stdout@0.2.0-rc-2023-12-05; - import wasi:cli/stderr@0.2.0-rc-2023-12-05; - import wasi:cli/terminal-input@0.2.0-rc-2023-12-05; - import wasi:cli/terminal-output@0.2.0-rc-2023-12-05; - import wasi:cli/terminal-stdin@0.2.0-rc-2023-12-05; - import wasi:cli/terminal-stdout@0.2.0-rc-2023-12-05; - import wasi:cli/terminal-stderr@0.2.0-rc-2023-12-05; - export wasi:cli/stdin@0.2.0-rc-2023-12-05; - export wasi:cli/stdout@0.2.0-rc-2023-12-05; - export wasi:cli/stderr@0.2.0-rc-2023-12-05; - export wasi:cli/terminal-input@0.2.0-rc-2023-12-05; - export wasi:cli/terminal-output@0.2.0-rc-2023-12-05; - export wasi:cli/terminal-stdin@0.2.0-rc-2023-12-05; - export wasi:cli/terminal-stdout@0.2.0-rc-2023-12-05; - export wasi:cli/terminal-stderr@0.2.0-rc-2023-12-05; - import wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10; - export wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10; - import wasi:http/types@0.2.0-rc-2023-10-18; - export wasi:http/types@0.2.0-rc-2023-10-18; - import wasi:http/outgoing-handler@0.2.0-rc-2023-10-18; - export wasi:http/outgoing-handler@0.2.0-rc-2023-10-18; - import wasi:sockets/ip-name-lookup@0.2.0-rc-2023-10-18; - export wasi:sockets/ip-name-lookup@0.2.0-rc-2023-10-18; - import wasi:sockets/tcp@0.2.0-rc-2023-10-18; - export wasi:sockets/tcp@0.2.0-rc-2023-10-18; - import wasi:sockets/udp@0.2.0-rc-2023-10-18; - export wasi:sockets/udp@0.2.0-rc-2023-10-18; + import wasi:cli/environment@0.2.0; + import wasi:filesystem/preopens@0.2.0; + import wasi:filesystem/types@0.2.0; + import wasi:io/error@0.2.0; + export wasi:io/error@0.2.0; + import wasi:io/streams@0.2.0; + export wasi:io/streams@0.2.0; + import wasi:io/poll@0.2.0; + export wasi:io/poll@0.2.0; + export wasi:cli/environment@0.2.0; + export wasi:filesystem/types@0.2.0; + export wasi:filesystem/preopens@0.2.0; + import wasi:cli/stdin@0.2.0; + import wasi:cli/stdout@0.2.0; + import wasi:cli/stderr@0.2.0; + import wasi:cli/terminal-input@0.2.0; + import wasi:cli/terminal-output@0.2.0; + import wasi:cli/terminal-stdin@0.2.0; + import wasi:cli/terminal-stdout@0.2.0; + import wasi:cli/terminal-stderr@0.2.0; + export wasi:cli/stdin@0.2.0; + export wasi:cli/stdout@0.2.0; + export wasi:cli/stderr@0.2.0; + export wasi:cli/terminal-input@0.2.0; + export wasi:cli/terminal-output@0.2.0; + export wasi:cli/terminal-stdin@0.2.0; + export wasi:cli/terminal-stdout@0.2.0; + export wasi:cli/terminal-stderr@0.2.0; + import wasi:clocks/monotonic-clock@0.2.0; + export wasi:clocks/monotonic-clock@0.2.0; + import wasi:http/types@0.2.0; + export wasi:http/types@0.2.0; + import wasi:http/outgoing-handler@0.2.0; + export wasi:http/outgoing-handler@0.2.0; + import wasi:sockets/ip-name-lookup@0.2.0; + export wasi:sockets/ip-name-lookup@0.2.0; + import wasi:sockets/tcp@0.2.0; + export wasi:sockets/tcp@0.2.0; + import wasi:sockets/udp@0.2.0; + export wasi:sockets/udp@0.2.0; } world virtual-base { } world virtual-io { - import wasi:io/error@0.2.0-rc-2023-11-10; - export wasi:io/error@0.2.0-rc-2023-11-10; - import wasi:io/streams@0.2.0-rc-2023-11-10; - export wasi:io/streams@0.2.0-rc-2023-11-10; - import wasi:io/poll@0.2.0-rc-2023-11-10; - export wasi:io/poll@0.2.0-rc-2023-11-10; + import wasi:io/error@0.2.0; + export wasi:io/error@0.2.0; + import wasi:io/streams@0.2.0; + export wasi:io/streams@0.2.0; + import wasi:io/poll@0.2.0; + export wasi:io/poll@0.2.0; } // io components of subsystems // where there is an intersection of // streams + poll world virtual-io-sockets { - import wasi:sockets/ip-name-lookup@0.2.0-rc-2023-10-18; - export wasi:sockets/ip-name-lookup@0.2.0-rc-2023-10-18; - import wasi:sockets/tcp@0.2.0-rc-2023-10-18; - export wasi:sockets/tcp@0.2.0-rc-2023-10-18; - import wasi:sockets/udp@0.2.0-rc-2023-10-18; - export wasi:sockets/udp@0.2.0-rc-2023-10-18; + import wasi:sockets/ip-name-lookup@0.2.0; + export wasi:sockets/ip-name-lookup@0.2.0; + import wasi:sockets/tcp@0.2.0; + export wasi:sockets/tcp@0.2.0; + import wasi:sockets/udp@0.2.0; + export wasi:sockets/udp@0.2.0; } world virtual-io-clocks { - import wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10; - export wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10; + import wasi:clocks/monotonic-clock@0.2.0; + export wasi:clocks/monotonic-clock@0.2.0; } world virtual-io-http { - import wasi:http/types@0.2.0-rc-2023-10-18; - export wasi:http/types@0.2.0-rc-2023-10-18; - import wasi:http/outgoing-handler@0.2.0-rc-2023-10-18; - export wasi:http/outgoing-handler@0.2.0-rc-2023-10-18; + import wasi:http/types@0.2.0; + export wasi:http/types@0.2.0; + import wasi:http/outgoing-handler@0.2.0; + export wasi:http/outgoing-handler@0.2.0; } world virtual-fs { - import wasi:filesystem/preopens@0.2.0-rc-2023-11-10; - import wasi:filesystem/types@0.2.0-rc-2023-11-10; - export wasi:filesystem/types@0.2.0-rc-2023-11-10; - export wasi:filesystem/preopens@0.2.0-rc-2023-11-10; + import wasi:filesystem/preopens@0.2.0; + import wasi:filesystem/types@0.2.0; + export wasi:filesystem/types@0.2.0; + export wasi:filesystem/preopens@0.2.0; } world virtual-stdio { - import wasi:cli/stdin@0.2.0-rc-2023-12-05; - import wasi:cli/stdout@0.2.0-rc-2023-12-05; - import wasi:cli/stderr@0.2.0-rc-2023-12-05; - import wasi:cli/terminal-input@0.2.0-rc-2023-12-05; - import wasi:cli/terminal-output@0.2.0-rc-2023-12-05; - import wasi:cli/terminal-stdin@0.2.0-rc-2023-12-05; - import wasi:cli/terminal-stdout@0.2.0-rc-2023-12-05; - import wasi:cli/terminal-stderr@0.2.0-rc-2023-12-05; - export wasi:cli/stdin@0.2.0-rc-2023-12-05; - export wasi:cli/stdout@0.2.0-rc-2023-12-05; - export wasi:cli/stderr@0.2.0-rc-2023-12-05; - export wasi:cli/terminal-input@0.2.0-rc-2023-12-05; - export wasi:cli/terminal-output@0.2.0-rc-2023-12-05; - export wasi:cli/terminal-stdin@0.2.0-rc-2023-12-05; - export wasi:cli/terminal-stdout@0.2.0-rc-2023-12-05; - export wasi:cli/terminal-stderr@0.2.0-rc-2023-12-05; + import wasi:cli/stdin@0.2.0; + import wasi:cli/stdout@0.2.0; + import wasi:cli/stderr@0.2.0; + import wasi:cli/terminal-input@0.2.0; + import wasi:cli/terminal-output@0.2.0; + import wasi:cli/terminal-stdin@0.2.0; + import wasi:cli/terminal-stdout@0.2.0; + import wasi:cli/terminal-stderr@0.2.0; + export wasi:cli/stdin@0.2.0; + export wasi:cli/stdout@0.2.0; + export wasi:cli/stderr@0.2.0; + export wasi:cli/terminal-input@0.2.0; + export wasi:cli/terminal-output@0.2.0; + export wasi:cli/terminal-stdin@0.2.0; + export wasi:cli/terminal-stdout@0.2.0; + export wasi:cli/terminal-stderr@0.2.0; } // remaining subsystems world virtual-env { - import wasi:cli/environment@0.2.0-rc-2023-12-05; - export wasi:cli/environment@0.2.0-rc-2023-12-05; + import wasi:cli/environment@0.2.0; + export wasi:cli/environment@0.2.0; } world virtual-clocks { - import wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10; - export wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10; - import wasi:clocks/wall-clock@0.2.0-rc-2023-11-10; - export wasi:clocks/wall-clock@0.2.0-rc-2023-11-10; + import wasi:clocks/monotonic-clock@0.2.0; + export wasi:clocks/monotonic-clock@0.2.0; + import wasi:clocks/wall-clock@0.2.0; + export wasi:clocks/wall-clock@0.2.0; } world virtual-random { - import wasi:random/insecure-seed@0.2.0-rc-2023-11-10; - import wasi:random/insecure@0.2.0-rc-2023-11-10; - import wasi:random/random@0.2.0-rc-2023-11-10; - export wasi:random/insecure-seed@0.2.0-rc-2023-11-10; - export wasi:random/insecure@0.2.0-rc-2023-11-10; - export wasi:random/random@0.2.0-rc-2023-11-10; + import wasi:random/insecure-seed@0.2.0; + import wasi:random/insecure@0.2.0; + import wasi:random/random@0.2.0; + export wasi:random/insecure-seed@0.2.0; + export wasi:random/insecure@0.2.0; + export wasi:random/random@0.2.0; } world virtual-sockets { - import wasi:sockets/ip-name-lookup@0.2.0-rc-2023-10-18; - export wasi:sockets/ip-name-lookup@0.2.0-rc-2023-10-18; - import wasi:sockets/tcp@0.2.0-rc-2023-10-18; - export wasi:sockets/tcp@0.2.0-rc-2023-10-18; - import wasi:sockets/udp@0.2.0-rc-2023-10-18; - export wasi:sockets/udp@0.2.0-rc-2023-10-18; - import wasi:sockets/instance-network@0.2.0-rc-2023-10-18; - import wasi:sockets/tcp-create-socket@0.2.0-rc-2023-10-18; - import wasi:sockets/udp-create-socket@0.2.0-rc-2023-10-18; - export wasi:sockets/instance-network@0.2.0-rc-2023-10-18; - export wasi:sockets/tcp-create-socket@0.2.0-rc-2023-10-18; - export wasi:sockets/udp-create-socket@0.2.0-rc-2023-10-18; - import wasi:sockets/network@0.2.0-rc-2023-10-18; - export wasi:sockets/network@0.2.0-rc-2023-10-18; + import wasi:sockets/ip-name-lookup@0.2.0; + export wasi:sockets/ip-name-lookup@0.2.0; + import wasi:sockets/tcp@0.2.0; + export wasi:sockets/tcp@0.2.0; + import wasi:sockets/udp@0.2.0; + export wasi:sockets/udp@0.2.0; + import wasi:sockets/instance-network@0.2.0; + import wasi:sockets/tcp-create-socket@0.2.0; + import wasi:sockets/udp-create-socket@0.2.0; + export wasi:sockets/instance-network@0.2.0; + export wasi:sockets/tcp-create-socket@0.2.0; + export wasi:sockets/udp-create-socket@0.2.0; + import wasi:sockets/network@0.2.0; + export wasi:sockets/network@0.2.0; } world virtual-http { - import wasi:http/types@0.2.0-rc-2023-10-18; - export wasi:http/types@0.2.0-rc-2023-10-18; - import wasi:http/incoming-handler@0.2.0-rc-2023-10-18; - import wasi:http/outgoing-handler@0.2.0-rc-2023-10-18; - export wasi:http/incoming-handler@0.2.0-rc-2023-10-18; - export wasi:http/outgoing-handler@0.2.0-rc-2023-10-18; + import wasi:http/types@0.2.0; + export wasi:http/types@0.2.0; + import wasi:http/incoming-handler@0.2.0; + import wasi:http/outgoing-handler@0.2.0; + export wasi:http/incoming-handler@0.2.0; + export wasi:http/outgoing-handler@0.2.0; } world virtual-exit { - import wasi:cli/exit@0.2.0-rc-2023-12-05; - export wasi:cli/exit@0.2.0-rc-2023-12-05; + import wasi:cli/exit@0.2.0; + export wasi:cli/exit@0.2.0; } world virt-test { - import wasi:clocks/wall-clock@0.2.0-rc-2023-11-10; - import wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10; - import wasi:filesystem/types@0.2.0-rc-2023-11-10; - import wasi:sockets/instance-network@0.2.0-rc-2023-10-18; - import wasi:sockets/ip-name-lookup@0.2.0-rc-2023-10-18; - import wasi:sockets/network@0.2.0-rc-2023-10-18; - import wasi:sockets/tcp-create-socket@0.2.0-rc-2023-10-18; - import wasi:sockets/tcp@0.2.0-rc-2023-10-18; - import wasi:sockets/udp-create-socket@0.2.0-rc-2023-10-18; - import wasi:sockets/udp@0.2.0-rc-2023-10-18; - import wasi:random/random@0.2.0-rc-2023-11-10; - import wasi:random/insecure@0.2.0-rc-2023-11-10; - import wasi:random/insecure-seed@0.2.0-rc-2023-11-10; - import wasi:io/poll@0.2.0-rc-2023-11-10; - import wasi:io/streams@0.2.0-rc-2023-11-10; - import wasi:cli/environment@0.2.0-rc-2023-12-05; - import wasi:filesystem/preopens@0.2.0-rc-2023-11-10; - import wasi:cli/exit@0.2.0-rc-2023-12-05; - import wasi:cli/stdin@0.2.0-rc-2023-12-05; - import wasi:cli/stdout@0.2.0-rc-2023-12-05; - import wasi:cli/stderr@0.2.0-rc-2023-12-05; - import wasi:cli/terminal-input@0.2.0-rc-2023-12-05; - import wasi:cli/terminal-output@0.2.0-rc-2023-12-05; - import wasi:cli/terminal-stdin@0.2.0-rc-2023-12-05; - import wasi:cli/terminal-stdout@0.2.0-rc-2023-12-05; - import wasi:cli/terminal-stderr@0.2.0-rc-2023-12-05; + import wasi:clocks/wall-clock@0.2.0; + import wasi:clocks/monotonic-clock@0.2.0; + import wasi:filesystem/types@0.2.0; + import wasi:sockets/instance-network@0.2.0; + import wasi:sockets/ip-name-lookup@0.2.0; + import wasi:sockets/network@0.2.0; + import wasi:sockets/tcp-create-socket@0.2.0; + import wasi:sockets/tcp@0.2.0; + import wasi:sockets/udp-create-socket@0.2.0; + import wasi:sockets/udp@0.2.0; + import wasi:random/random@0.2.0; + import wasi:random/insecure@0.2.0; + import wasi:random/insecure-seed@0.2.0; + import wasi:io/poll@0.2.0; + import wasi:io/streams@0.2.0; + import wasi:cli/environment@0.2.0; + import wasi:filesystem/preopens@0.2.0; + import wasi:cli/exit@0.2.0; + import wasi:cli/stdin@0.2.0; + import wasi:cli/stdout@0.2.0; + import wasi:cli/stderr@0.2.0; + import wasi:cli/terminal-input@0.2.0; + import wasi:cli/terminal-output@0.2.0; + import wasi:cli/terminal-stdin@0.2.0; + import wasi:cli/terminal-stdout@0.2.0; + import wasi:cli/terminal-stderr@0.2.0; export test-get-env: func() -> list>; export test-file-read: func(path: string) -> string;