diff --git a/Cargo.lock b/Cargo.lock index 959390b..9051680 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -97,6 +97,73 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "axum" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18ed336352031311f4e0b4dd2ff392d4fbb370777c9d18d7fc9d7359f73871" +dependencies = [ + "axum-core", + "axum-macros", + "bytes", + "futures-util", + "http 1.3.1", + "http-body 1.0.1", + "http-body-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "sync_wrapper 1.0.2", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22" +dependencies = [ + "bytes", + "futures-core", + "http 1.3.1", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper 1.0.2", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "axum-router" +version = "0.1.0" +dependencies = [ + "axum", + "serde", + "spin-sdk", + "tower-service", +] + [[package]] name = "backtrace" version = "0.3.69" @@ -162,9 +229,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cap-fs-ext" @@ -281,7 +348,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" dependencies = [ - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -850,7 +917,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http 1.0.0", + "http 1.3.1", "indexmap", "slab", "tokio", @@ -879,7 +946,7 @@ name = "hello-world" version = "0.1.0" dependencies = [ "anyhow", - "http 1.0.0", + "http 1.3.1", "serde", "spin-sdk", ] @@ -918,9 +985,9 @@ dependencies = [ [[package]] name = "http" -version = "1.0.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", @@ -940,24 +1007,24 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.0.0", + "http 1.3.1", ] [[package]] name = "http-body-util" -version = "0.1.0" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", - "futures-util", - "http 1.0.0", - "http-body 1.0.0", + "futures-core", + "http 1.3.1", + "http-body 1.0.1", "pin-project-lite", ] @@ -966,7 +1033,7 @@ name = "http-hello" version = "0.1.0" dependencies = [ "anyhow", - "http 1.0.0", + "http 1.3.1", "spin-sdk", ] @@ -1040,8 +1107,8 @@ dependencies = [ "futures-channel", "futures-util", "h2 0.4.2", - "http 1.0.0", - "http-body 1.0.0", + "http 1.3.1", + "http-body 1.0.1", "httparse", "httpdate", "itoa", @@ -1195,7 +1262,7 @@ name = "json-http-rust" version = "0.1.0" dependencies = [ "anyhow", - "http 1.0.0", + "http 1.3.1", "serde", "spin-sdk", ] @@ -1268,6 +1335,12 @@ dependencies = [ "libc", ] +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "maybe-owned" version = "0.3.4" @@ -1720,7 +1793,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "system-configuration", "tokio", "tokio-native-tls", @@ -1761,7 +1834,7 @@ name = "rust-key-value" version = "0.1.0" dependencies = [ "anyhow", - "http 1.0.0", + "http 1.3.1", "spin-sdk", ] @@ -1778,7 +1851,7 @@ name = "rust-outbound-mysql" version = "0.1.0" dependencies = [ "anyhow", - "http 1.0.0", + "http 1.3.1", "serde", "serde_json", "spin-sdk", @@ -1789,7 +1862,7 @@ name = "rust-outbound-pg" version = "0.1.0" dependencies = [ "anyhow", - "http 1.0.0", + "http 1.3.1", "spin-sdk", ] @@ -1799,7 +1872,7 @@ version = "0.1.0" dependencies = [ "anyhow", "chrono", - "http 1.0.0", + "http 1.3.1", "spin-sdk", ] @@ -1808,7 +1881,7 @@ name = "rust-outbound-pg-v4" version = "0.1.0" dependencies = [ "anyhow", - "http 1.0.0", + "http 1.3.1", "spin-sdk", ] @@ -1974,20 +2047,40 @@ dependencies = [ "serde", ] +[[package]] +name = "send-request" +version = "0.1.0" +dependencies = [ + "anyhow", + "axum", + "http 1.3.1", + "spin-sdk", +] + [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -2005,6 +2098,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + [[package]] name = "serde_spanned" version = "0.6.9" @@ -2057,7 +2161,7 @@ name = "simple-http" version = "0.1.0" dependencies = [ "anyhow", - "http 1.0.0", + "http 1.3.1", "spin-sdk", ] @@ -2148,7 +2252,7 @@ dependencies = [ "chrono", "form_urlencoded", "futures", - "http 1.0.0", + "http 1.3.1", "http-body-util", "hyper 1.2.0", "once_cell", @@ -2160,15 +2264,17 @@ dependencies = [ "serde_json", "spin-executor", "spin-macro", - "thiserror 1.0.57", + "spin-wasip3-http", + "spin-wasip3-http-macro", + "thiserror 2.0.17", "tokio", "uuid", "wasi 0.13.1+wasi-0.2.0", "wasmtime", "wasmtime-wasi", "wasmtime-wasi-http", - "wit-bindgen", - "wit-component", + "wit-bindgen 0.43.0", + "wit-component 0.235.0", ] [[package]] @@ -2191,6 +2297,27 @@ dependencies = [ "url", ] +[[package]] +name = "spin-wasip3-http" +version = "5.0.0" +dependencies = [ + "anyhow", + "bytes", + "http 1.3.1", + "http-body 1.0.1", + "http-body-util", + "wasip3", +] + +[[package]] +name = "spin-wasip3-http-macro" +version = "5.0.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "spinredis" version = "0.1.0" @@ -2257,6 +2384,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + [[package]] name = "system-configuration" version = "0.5.1" @@ -2332,11 +2465,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.17", ] [[package]] @@ -2352,9 +2485,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", @@ -2481,11 +2614,31 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 1.0.2", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -2664,6 +2817,26 @@ dependencies = [ "url", ] +[[package]] +name = "wasip3" +version = "0.2.1+wasi-0.3.0-rc-2025-09-16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbb2796323e2357ae2d4ba2b781a0392b533f40a5b9f534eef49b23e54186d64" +dependencies = [ + "bytes", + "http 1.3.1", + "http-body 1.0.1", + "thiserror 2.0.17", + "wit-bindgen 0.46.0", +] + +[[package]] +name = "wasip3-hello-world" +version = "0.1.0" +dependencies = [ + "spin-sdk", +] + [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -2741,7 +2914,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3bc393c395cb621367ff02d854179882b9a351b4e0c93d1397e6090b53a5c2a" dependencies = [ "leb128fmt", - "wasmparser", + "wasmparser 0.235.0", +] + +[[package]] +name = "wasm-encoder" +version = "0.239.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be00faa2b4950c76fe618c409d2c3ea5a3c9422013e079482d78544bb2d184c" +dependencies = [ + "leb128fmt", + "wasmparser 0.239.0", ] [[package]] @@ -2752,8 +2935,20 @@ checksum = "b055604ba04189d54b8c0ab2c2fc98848f208e103882d5c0b984f045d5ea4d20" dependencies = [ "anyhow", "indexmap", - "wasm-encoder", - "wasmparser", + "wasm-encoder 0.235.0", + "wasmparser 0.235.0", +] + +[[package]] +name = "wasm-metadata" +version = "0.239.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20b3ec880a9ac69ccd92fbdbcf46ee833071cf09f82bb005b2327c7ae6025ae2" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder 0.239.0", + "wasmparser 0.239.0", ] [[package]] @@ -2769,6 +2964,18 @@ dependencies = [ "serde", ] +[[package]] +name = "wasmparser" +version = "0.239.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9d90bb93e764f6beabf1d02028c70a2156a6583e63ac4218dd07ef733368b0" +dependencies = [ + "bitflags 2.4.2", + "hashbrown", + "indexmap", + "semver", +] + [[package]] name = "wasmprinter" version = "0.235.0" @@ -2777,7 +2984,7 @@ checksum = "75aa8e9076de6b9544e6dab4badada518cca0bf4966d35b131bbd057aed8fa0a" dependencies = [ "anyhow", "termcolor", - "wasmparser", + "wasmparser 0.235.0", ] [[package]] @@ -2816,8 +3023,8 @@ dependencies = [ "smallvec", "target-lexicon", "trait-variant", - "wasm-encoder", - "wasmparser", + "wasm-encoder 0.235.0", + "wasmparser 0.235.0", "wasmtime-environ", "wasmtime-internal-asm-macros", "wasmtime-internal-cache", @@ -2857,8 +3064,8 @@ dependencies = [ "serde_derive", "smallvec", "target-lexicon", - "wasm-encoder", - "wasmparser", + "wasm-encoder 0.235.0", + "wasmparser 0.235.0", "wasmprinter", "wasmtime-internal-component-util", ] @@ -2904,7 +3111,7 @@ dependencies = [ "syn 2.0.104", "wasmtime-internal-component-util", "wasmtime-internal-wit-bindgen", - "wit-parser", + "wit-parser 0.235.0", ] [[package]] @@ -2933,8 +3140,8 @@ dependencies = [ "pulley-interpreter", "smallvec", "target-lexicon", - "thiserror 2.0.12", - "wasmparser", + "thiserror 2.0.17", + "wasmparser 0.235.0", "wasmtime-environ", "wasmtime-internal-math", "wasmtime-internal-versioned-export-macros", @@ -3030,7 +3237,7 @@ dependencies = [ "gimli 0.31.1", "object 0.36.7", "target-lexicon", - "wasmparser", + "wasmparser 0.235.0", "wasmtime-environ", "wasmtime-internal-cranelift", "winch-codegen", @@ -3045,7 +3252,7 @@ dependencies = [ "anyhow", "heck", "indexmap", - "wit-parser", + "wit-parser 0.235.0", ] [[package]] @@ -3069,7 +3276,7 @@ dependencies = [ "io-lifetimes", "rustix 1.0.8", "system-interface", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tracing", "url", @@ -3089,8 +3296,8 @@ dependencies = [ "async-trait", "bytes", "futures", - "http 1.0.0", - "http-body 1.0.0", + "http 1.3.1", + "http-body 1.0.1", "http-body-util", "hyper 1.2.0", "rustls", @@ -3135,7 +3342,7 @@ dependencies = [ "leb128fmt", "memchr", "unicode-width", - "wasm-encoder", + "wasm-encoder 0.235.0", ] [[package]] @@ -3184,7 +3391,7 @@ dependencies = [ "anyhow", "async-trait", "bitflags 2.4.2", - "thiserror 2.0.12", + "thiserror 2.0.17", "tracing", "wasmtime", "wiggle-macro", @@ -3260,8 +3467,8 @@ dependencies = [ "regalloc2", "smallvec", "target-lexicon", - "thiserror 2.0.12", - "wasmparser", + "thiserror 2.0.17", + "wasmparser 0.235.0", "wasmtime-environ", "wasmtime-internal-cranelift", "wasmtime-internal-math", @@ -3460,7 +3667,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a18712ff1ec5bd09da500fe1e91dec11256b310da0ff33f8b4ec92b927cf0c6" dependencies = [ "wit-bindgen-rt 0.43.0", - "wit-bindgen-rust-macro", + "wit-bindgen-rust-macro 0.43.0", +] + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +dependencies = [ + "bitflags 2.4.2", + "futures", + "once_cell", + "wit-bindgen-rust-macro 0.46.0", ] [[package]] @@ -3471,7 +3690,18 @@ checksum = "2c53468e077362201de11999c85c07c36e12048a990a3e0d69da2bd61da355d0" dependencies = [ "anyhow", "heck", - "wit-parser", + "wit-parser 0.235.0", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cabd629f94da277abc739c71353397046401518efb2c707669f805205f0b9890" +dependencies = [ + "anyhow", + "heck", + "wit-parser 0.239.0", ] [[package]] @@ -3514,9 +3744,25 @@ dependencies = [ "indexmap", "prettyplease", "syn 2.0.104", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", + "wasm-metadata 0.235.0", + "wit-bindgen-core 0.43.0", + "wit-component 0.235.0", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a4232e841089fa5f3c4fc732a92e1c74e1a3958db3b12f1de5934da2027f1f4" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn 2.0.104", + "wasm-metadata 0.239.0", + "wit-bindgen-core 0.46.0", + "wit-component 0.239.0", ] [[package]] @@ -3530,8 +3776,23 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.104", - "wit-bindgen-core", - "wit-bindgen-rust", + "wit-bindgen-core 0.43.0", + "wit-bindgen-rust 0.43.0", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0d4698c2913d8d9c2b220d116409c3f51a7aa8d7765151b886918367179ee9" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.104", + "wit-bindgen-core 0.46.0", + "wit-bindgen-rust 0.46.0", ] [[package]] @@ -3547,10 +3808,29 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", + "wasm-encoder 0.235.0", + "wasm-metadata 0.235.0", + "wasmparser 0.235.0", + "wit-parser 0.235.0", +] + +[[package]] +name = "wit-component" +version = "0.239.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a866b19dba2c94d706ec58c92a4c62ab63e482b4c935d2a085ac94caecb136" +dependencies = [ + "anyhow", + "bitflags 2.4.2", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder 0.239.0", + "wasm-metadata 0.239.0", + "wasmparser 0.239.0", + "wit-parser 0.239.0", ] [[package]] @@ -3568,7 +3848,25 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser", + "wasmparser 0.235.0", +] + +[[package]] +name = "wit-parser" +version = "0.239.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55c92c939d667b7bf0c6bf2d1f67196529758f99a2a45a3355cc56964fd5315d" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser 0.239.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e4e9694..6ebca34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ The Spin Rust SDK makes it easy to build Spin components in Rust. name = "spin_sdk" [dependencies] -anyhow = "1" +anyhow = { workspace = true } async-trait = "0.1.74" chrono = "0.4.38" form_urlencoded = "1.0" @@ -25,14 +25,16 @@ postgres_range = { version = "0.11.1", optional = true } rust_decimal = { version = "1.37.2", default-features = false, optional = true } spin-executor = { version = "5.0.0", path = "crates/executor" } spin-macro = { version = "5.0.0", path = "crates/macro" } -thiserror = "1.0.37" +spin-wasip3-http = { version = "5.0.0", path = "crates/spin-wasip3-http", optional = true } +spin-wasip3-http-macro = { version = "5.0.0", path = "crates/spin-wasip3-http-macro", optional = true } +thiserror = { workspace = true } uuid = { version = "1.18.0", optional = true } wit-bindgen = { workspace = true } routefinder = "0.5.3" once_cell = { workspace = true } futures = { workspace = true } -bytes = "1" -hyperium = { package = "http", version = "1.0.0" } +bytes = { workspace = true } +hyperium = { workspace = true } serde_json = { version = "1.0.96", optional = true } serde = { version = "1.0.163", optional = true } wasi = { workspace = true } @@ -42,6 +44,7 @@ default = ["export-sdk-language", "json", "postgres4-types"] export-sdk-language = [] json = ["dep:serde", "dep:serde_json"] postgres4-types = ["dep:rust_decimal", "dep:uuid", "dep:postgres_range", "json"] +wasip3-unstable = ["dep:spin-wasip3-http", "dep:spin-wasip3-http-macro"] [workspace] resolver = "2" @@ -65,6 +68,9 @@ members = [ "examples/variables", "examples/wasi-http-streaming-outgoing-body", "examples/wasi-http-streaming-file", + "examples/wasip3-http-axum-router", + "examples/wasip3-http-hello-world", + "examples/wasip3-http-send-request", "test-cases/simple-http", "test-cases/simple-redis", "crates/*", @@ -92,12 +98,18 @@ authors = ["Spin Framework Maintainers "] edition = "2021" license = "Apache-2.0 WITH LLVM-exception" repository = "https://github.com/spinframework/spin-rust-sdk" -rust-version = "1.78" +rust-version = "1.86" homepage = "https://spinframework.dev/rust-components" [workspace.dependencies] +anyhow = "1" +hyperium = { package = "http", version = "1.3.1" } +http-body = "1.0.1" +http-body-util = "0.1.3" +bytes = "1.10.1" wit-bindgen = "0.43.0" futures = "0.3.28" once_cell = "1.18.0" +thiserror = "2.0.17" # Pin to the last version that targeted WASI 0.2.0 wasi = "=0.13.1" diff --git a/crates/spin-wasip3-http-macro/Cargo.toml b/crates/spin-wasip3-http-macro/Cargo.toml new file mode 100644 index 0000000..9ebc492 --- /dev/null +++ b/crates/spin-wasip3-http-macro/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "spin-wasip3-http-macro" +version.workspace = true +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +homepage.workspace = true +description = """ +Rust procedural macros for Spin and WASIp3 +""" + +[lib] +name = "spin_wasip3_http_macro" +proc-macro = true + +[dependencies] +proc-macro2 = "1" +quote = "1.0" +syn = { version = "1.0", features = [ "full" ]} \ No newline at end of file diff --git a/crates/spin-wasip3-http-macro/src/lib.rs b/crates/spin-wasip3-http-macro/src/lib.rs new file mode 100644 index 0000000..448797f --- /dev/null +++ b/crates/spin-wasip3-http-macro/src/lib.rs @@ -0,0 +1,74 @@ +use proc_macro::TokenStream; +use quote::quote; + +/// Marks an `async fn` as an HTTP component entrypoint for Spin. +/// +/// The `#[http_service]` attribute designates an asynchronous function as the +/// handler for incoming HTTP requests in a Spin component using the WASI Preview 3 +/// (`wasip3`) HTTP ABI. +/// +/// When applied, this macro generates the necessary boilerplate to export the +/// function to the Spin runtime as a valid HTTP handler. The function must be +/// declared `async` and take a single argument implementing +/// [`FromRequest`](::spin_sdk::http_wasip3::FromRequest), typically +/// [`Request`](::spin_sdk::http_wasip3::Request), and must return a type that +/// implements [`IntoResponse`](::spin_sdk::http_wasip3::IntoResponse). +/// +/// # Requirements +/// +/// - The annotated function **must** be `async`. +/// - The function’s parameter type must implement [`FromRequest`]. +/// - The return type must implement [`IntoResponse`]. +/// +/// If the function is not asynchronous, the macro emits a compile-time error. +/// +/// # Example +/// +/// ```ignore +/// use spin_sdk::http_wasip3::{http_service, Request, IntoResponse}; +/// +/// #[http_service] +/// async fn my_handler(request: Request) -> impl IntoResponse { +/// // Your logic goes here +/// } +/// ``` +/// +/// # Generated Code +/// +/// The macro expands into a module containing a `Spin` struct that implements the +/// WASI `http.handler/Guest` interface, wiring the annotated function as the +/// handler’s entrypoint. This allows the function to be invoked automatically +/// by the Spin runtime when HTTP requests are received. +#[proc_macro_attribute] +pub fn http_service(_attr: TokenStream, item: TokenStream) -> TokenStream { + let func = syn::parse_macro_input!(item as syn::ItemFn); + + if func.sig.asyncness.is_none() { + return syn::Error::new_spanned( + func.sig.fn_token, + "the `#[http_component]` function must be `async`", + ) + .to_compile_error() + .into(); + } + + let func_name = &func.sig.ident; + + quote!( + #func + mod __spin_wasip3_http { + use ::spin_sdk::http_wasip3::IntoResponse; + + struct Spin; + ::spin_sdk::http_wasip3::wasip3::http::proxy::export!(Spin); + + impl ::spin_sdk::http_wasip3::wasip3::exports::http::handler::Guest for self::Spin { + async fn handle(request: ::spin_sdk::http_wasip3::wasip3::http::types::Request) -> Result<::spin_sdk::http_wasip3::wasip3::http::types::Response, ::spin_sdk::http_wasip3::wasip3::http::types::ErrorCode> { + let request = <::spin_sdk::http_wasip3::Request as ::spin_sdk::http_wasip3::FromRequest>::from_request(request)?; + ::spin_sdk::http_wasip3::IntoResponse::into_response(super::#func_name(request).await) + } + } + } + ) + .into() +} diff --git a/crates/spin-wasip3-http/Cargo.toml b/crates/spin-wasip3-http/Cargo.toml new file mode 100644 index 0000000..2b85aba --- /dev/null +++ b/crates/spin-wasip3-http/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "spin-wasip3-http" +version.workspace = true +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +homepage.workspace = true + +[dependencies] +anyhow = { workspace = true } +bytes = { workspace = true } +http-body = { workspace = true } +http-body-util = { workspace = true } +hyperium = { workspace = true } +wasip3 = { version = "0.2.1", features = ["http-compat"] } \ No newline at end of file diff --git a/crates/spin-wasip3-http/src/lib.rs b/crates/spin-wasip3-http/src/lib.rs new file mode 100644 index 0000000..4b58e18 --- /dev/null +++ b/crates/spin-wasip3-http/src/lib.rs @@ -0,0 +1,385 @@ +//! Experimental Rust SDK for WASIp3 http. + +#![deny(missing_docs)] + +#[doc(hidden)] +pub use wasip3; + +use hyperium as http; +use std::any::Any; +pub use wasip3::http_compat::{Request, Response}; +use wasip3::{ + http::types, + http_compat::{ + http_from_wasi_request, http_from_wasi_response, http_into_wasi_request, + http_into_wasi_response, + }, +}; + +/// A alias for [`std::result::Result`] that uses [`Error`] as the default error type. +/// +/// This allows functions throughout the crate to return `Result` +/// instead of writing out `Result` explicitly. +pub type Result = ::std::result::Result; + +type HttpResult = Result; + +/// The error type used for HTTP operations within the WASI environment. +/// +/// This enum provides a unified representation of all errors that can occur +/// during HTTP request or response handling, whether they originate from +/// WASI-level error codes, dynamic runtime failures, or full HTTP responses +/// returned as error results. +/// +/// # See also +/// - [`http::Error`]: Error type originating from the [`http`] crate. +/// - [`wasip3::http::types::ErrorCode`]: Standard WASI HTTP error codes. +/// - [`wasip3::http::types::Response`]: Used when an error represents an HTTP response body. +#[derive(Debug)] +pub enum Error { + /// A low-level WASI HTTP error code. + /// + /// Wraps [`wasip3::http::types::ErrorCode`] to represent + /// transport-level or protocol-level failures. + ErrorCode(wasip3::http::types::ErrorCode), + /// An error originating from the [`http`] crate. + /// + /// Covers errors encountered during the construction, + /// parsing, or validation of [`http`] types (e.g. invalid headers, + /// malformed URIs, or protocol violations). + HttpError(http::Error), + /// A dynamic application or library error. + /// + /// Used for any runtime error that implements [`std::error::Error`], + /// allowing flexibility for different error sources. + Other(Box), + /// An HTTP response treated as an error. + /// + /// Contains a full [`wasip3::http::types::Response`], such as + /// a `404 Not Found` or `500 Internal Server Error`, when + /// the response itself represents an application-level failure. + Response(wasip3::http::types::Response), +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Error::ErrorCode(e) => write!(f, "{e}"), + Error::HttpError(e) => write!(f, "{e}"), + Error::Other(e) => write!(f, "{e}"), + Error::Response(resp) => match http::StatusCode::from_u16(resp.get_status_code()) { + Ok(status) => write!(f, "{status}"), + Err(_) => write!(f, "invalid status code {}", resp.get_status_code()), + }, + } + } +} + +impl std::error::Error for Error {} + +impl From for Error { + fn from(err: http::Error) -> Error { + Error::HttpError(err) + } +} + +impl From for Error { + fn from(err: anyhow::Error) -> Error { + match err.downcast::() { + Ok(code) => Error::ErrorCode(code), + Err(other) => match other.downcast::() { + Ok(err) => err, + Err(other) => Error::Other(other.into_boxed_dyn_error()), + }, + } + } +} + +impl From for Error { + fn from(v: std::convert::Infallible) -> Self { + match v {} + } +} + +impl From for Error { + fn from(code: types::ErrorCode) -> Self { + Error::ErrorCode(code) + } +} + +impl From for Error { + fn from(resp: types::Response) -> Self { + Error::Response(resp) + } +} + +impl> IntoResponse for Result { + fn into_response(self) -> HttpResult { + match self { + Ok(ok) => ok.into_response(), + Err(err) => match err.into() { + Error::ErrorCode(code) => Err(code), + Error::Response(resp) => Ok(resp), + Error::HttpError(err) => match err { + err if err.is::() => { + Err(types::ErrorCode::HttpRequestMethodInvalid) + } + err if err.is::() => { + Err(types::ErrorCode::HttpRequestUriInvalid) + } + err => Err(types::ErrorCode::InternalError(Some(err.to_string()))), + }, + Error::Other(other) => { + Err(types::ErrorCode::InternalError(Some(other.to_string()))) + } + }, + } + } +} + +/// Sends an HTTP request and returns the corresponding [`wasip3::http::types::Response`]. +/// +/// This function converts the provided value into a [`wasip3::http::types::Request`] using the +/// [`IntoRequest`] trait, dispatches it to the WASI HTTP handler, and awaits +/// the resulting response. It provides a convenient high-level interface for +/// issuing HTTP requests within a WASI environment. +pub async fn send(request: impl IntoRequest) -> HttpResult { + let request = request.into_request()?; + let response = wasip3::http::handler::handle(request).await?; + Response::from_response(response) +} + +/// A body type representing an empty payload. +/// +/// This is a convenience alias for [`http_body_util::Empty`], +/// used when constructing HTTP requests or responses with no body. +/// +/// # Examples +/// +/// ```ignore +/// use spin_wasip3_http::EmptyBody; +/// +/// let empty = EmptyBody::new(); +/// let response = http::Response::builder() +/// .status(204) +/// .body(empty) +/// .unwrap(); +/// ``` +pub type EmptyBody = http_body_util::Empty; + +/// A body type representing a complete, in-memory payload. +/// +/// This is a convenience alias for [`http_body_util::Full`], used when the +/// entire body is already available as a single value of type `T`. +/// +/// It is typically used for sending small or pre-buffered request or response +/// bodies without the need for streaming. +/// +/// # Examples +/// +/// ```ignore +/// use spin_wasip3_http::FullBody; +/// use bytes::Bytes; +/// +/// let body = FullBody::new(Bytes::from("hello")); +/// let request = http::Request::builder() +/// .method("POST") +/// .uri("https://example.com") +/// .body(body) +/// .unwrap(); +/// ``` +pub type FullBody = http_body_util::Full; + +/// A trait for constructing a value from a [`wasip3::http::types::Request`]. +/// +/// This is the inverse of [`IntoRequest`], allowing higher-level request +/// types to be built from standardized WASI HTTP requests—for example, +/// to parse structured payloads, extract query parameters, or perform +/// request validation. +/// +/// # See also +/// - [`IntoRequest`]: Converts a type into a [`wasip3::http::types::Request`]. +pub trait FromRequest { + /// Attempts to construct `Self` from a [`wasip3::http::types::Request`]. + fn from_request(req: wasip3::http::types::Request) -> HttpResult + where + Self: Sized; +} + +impl FromRequest for types::Request { + fn from_request(req: types::Request) -> HttpResult { + Ok(req) + } +} + +impl FromRequest for Request { + fn from_request(req: types::Request) -> HttpResult { + http_from_wasi_request(req) + } +} + +/// A trait for any type that can be converted into a [`wasip3::http::types::Request`]. +/// +/// This trait provides a unified interface for adapting user-defined request +/// types into the lower-level [`wasip3::http::types::Request`] format used by +/// the WASI HTTP subsystem. +/// +/// Implementing `IntoRequest` allows custom builders or wrapper types to +/// interoperate seamlessly with APIs that expect standardized WASI HTTP +/// request objects. +/// +/// # See also +/// - [`FromRequest`]: The inverse conversion trait. +pub trait IntoRequest { + /// Converts `self` into a [`wasip3::http::types::Request`]. + fn into_request(self) -> HttpResult; +} + +impl IntoRequest for http::Request +where + T: http_body::Body + Any, + T::Data: Into>, + T::Error: Into>, +{ + fn into_request(self) -> HttpResult { + http_into_wasi_request(self) + } +} + +/// A trait for constructing a value from a [`wasip3::http::types::Response`]. +/// +/// This is the inverse of [`IntoResponse`], allowing higher-level response +/// types to be derived from standardized WASI HTTP responses—for example, +/// to deserialize JSON payloads or map responses to domain-specific types. +/// +/// # See also +/// - [`IntoResponse`]: Converts a type into a [`wasip3::http::types::Response`]. +pub trait FromResponse { + /// Attempts to construct `Self` from a [`wasip3::http::types::Response`]. + fn from_response(response: wasip3::http::types::Response) -> HttpResult + where + Self: Sized; +} + +impl FromResponse for Response { + fn from_response(resp: types::Response) -> HttpResult { + http_from_wasi_response(resp) + } +} + +/// A trait for any type that can be converted into a [`wasip3::http::types::Response`]. +/// +/// This trait provides a unified interface for adapting user-defined response +/// types into the lower-level [`wasip3::http::types::Response`] format used by +/// the WASI HTTP subsystem. +/// +/// Implementing `IntoResponse` enables ergonomic conversion from domain-level +/// response types or builders into standardized WASI HTTP responses. +/// +/// # See also +/// - [`FromResponse`]: The inverse conversion trait. +pub trait IntoResponse { + /// Converts `self` into a [`wasip3::http::types::Response`]. + fn into_response(self) -> HttpResult; +} + +impl IntoResponse for types::Response { + fn into_response(self) -> HttpResult { + Ok(self) + } +} + +impl IntoResponse for (http::StatusCode, T) +where + T: http_body::Body + Any, + T::Data: Into>, + T::Error: Into>, +{ + fn into_response(self) -> HttpResult { + http_into_wasi_response( + http::Response::builder() + .status(self.0) + .body(self.1) + .unwrap(), + ) + } +} + +impl IntoResponse for &'static str { + fn into_response(self) -> HttpResult { + http::Response::new(http_body_util::Full::new(self.as_bytes())).into_response() + } +} + +impl IntoResponse for String { + fn into_response(self) -> HttpResult { + http::Response::new(self).into_response() + } +} + +impl IntoResponse for http::Response +where + T: http_body::Body + Any, + T::Data: Into>, + T::Error: Into>, +{ + fn into_response(self) -> HttpResult { + http_into_wasi_response(self) + } +} + +/// Helpers for consuming an [`IncomingBody`]. +/// +/// This module provides extension traits and utilities for working with +/// [`IncomingBody`] instances, such as streaming or collecting the entire +/// body into memory. +/// +/// These helpers make it easier to transform low-level streaming body types +/// into higher-level forms (e.g., [`Bytes`]) for simplified data handling. +pub mod body { + use bytes::Bytes; + use http_body_util::{BodyDataStream, BodyExt}; + use wasip3::{ + http::types::ErrorCode, + http_compat::{IncomingBody, IncomingMessage}, + }; + + /// Extension trait providing convenient methods for consuming an [`IncomingBody`]. + /// + /// This trait defines common patterns for handling HTTP body data in + /// asynchronous contexts. It allows converting the body into a stream + /// or fully collecting it into memory as a [`Bytes`] buffer. + #[allow(async_fn_in_trait)] + pub trait IncomingBodyExt { + /// Convert this [`IncomingBody`] into a [`BodyDataStream`]. + /// + /// This method enables iteration over the body’s data chunks as they + /// arrive, without collecting them all into memory at once. It is + /// suitable for processing large or streaming payloads efficiently. + fn stream(self) -> BodyDataStream + where + Self: Sized; + + /// Consume this [`IncomingBody`] and collect it into a single [`Bytes`] buffer. + /// + /// This method reads the entire body asynchronously and returns the + /// concatenated contents. It is best suited for small or bounded-size + /// payloads where holding all data in memory is acceptable. + async fn bytes(self) -> Result; + } + + impl IncomingBodyExt for IncomingBody { + /// Convert this [`IncomingBody`] into a [`BodyDataStream`]. + fn stream(self) -> BodyDataStream + where + Self: Sized, + { + BodyDataStream::new(self) + } + + /// Collect the [`IncomingBody`] into a single [`Bytes`] buffer. + async fn bytes(self) -> Result { + self.collect().await.map(|c| c.to_bytes()) + } + } +} diff --git a/examples/wasip3-concurrent-outbound-http-calls/.gitignore b/examples/wasip3-concurrent-outbound-http-calls/.gitignore new file mode 100644 index 0000000..386474f --- /dev/null +++ b/examples/wasip3-concurrent-outbound-http-calls/.gitignore @@ -0,0 +1,2 @@ +target/ +.spin/ diff --git a/examples/wasip3-concurrent-outbound-http-calls/Cargo.lock b/examples/wasip3-concurrent-outbound-http-calls/Cargo.lock new file mode 100644 index 0000000..ab2e228 --- /dev/null +++ b/examples/wasip3-concurrent-outbound-http-calls/Cargo.lock @@ -0,0 +1,1368 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[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 = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.107", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cc" +version = "1.2.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "concurrent-outbound-http-calls" +version = "0.1.0" +dependencies = [ + "anyhow", + "futures", + "http", + "spin-sdk", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "find-msvc-tools" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.107", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "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" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + +[[package]] +name = "indexmap" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +dependencies = [ + "equivalent", + "hashbrown 0.16.0", + "serde", + "serde_core", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "postgres-protocol" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbef655056b916eb868048276cfd5d6a7dea4f81560dfd047f97c8c6fe3fcfd4" +dependencies = [ + "base64", + "byteorder", + "bytes", + "fallible-iterator", + "hmac", + "md-5", + "memchr", + "rand", + "sha2", + "stringprep", +] + +[[package]] +name = "postgres-types" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef4605b7c057056dd35baeb6ac0c0338e4975b1f2bef0f65da953285eb007095" +dependencies = [ + "bytes", + "fallible-iterator", + "postgres-protocol", +] + +[[package]] +name = "postgres_range" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6dce28dc5ba143d8eb157b62aac01ae5a1c585c40792158b720e86a87642101" +dependencies = [ + "postgres-protocol", + "postgres-types", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.107", +] + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom", +] + +[[package]] +name = "routefinder" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0971d3c8943a6267d6bd0d782fdc4afa7593e7381a92a3df950ff58897e066b5" +dependencies = [ + "smartcow", + "smartstring", +] + +[[package]] +name = "rust_decimal" +version = "1.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35affe401787a9bd846712274d97654355d21b2a2c092a3139aabe31e9022282" +dependencies = [ + "arrayvec", + "num-traits", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.107", +] + +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "smartcow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "656fcb1c1fca8c4655372134ce87d8afdf5ec5949ebabe8d314be0141d8b5da2" +dependencies = [ + "smartstring", +] + +[[package]] +name = "smartstring" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" +dependencies = [ + "autocfg", + "static_assertions", + "version_check", +] + +[[package]] +name = "spin-executor" +version = "5.0.0" +dependencies = [ + "futures", + "once_cell", + "wasi", +] + +[[package]] +name = "spin-macro" +version = "5.0.0" +dependencies = [ + "anyhow", + "bytes", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "spin-sdk" +version = "5.0.0" +dependencies = [ + "anyhow", + "async-trait", + "bytes", + "chrono", + "form_urlencoded", + "futures", + "http", + "once_cell", + "postgres_range", + "routefinder", + "rust_decimal", + "serde", + "serde_json", + "spin-executor", + "spin-macro", + "spin-wasip3-http", + "spin-wasip3-http-macro", + "thiserror", + "uuid", + "wasi", + "wit-bindgen 0.43.0", +] + +[[package]] +name = "spin-wasip3-http" +version = "5.0.0" +dependencies = [ + "anyhow", + "bytes", + "http", + "http-body", + "http-body-util", + "wasip3", +] + +[[package]] +name = "spin-wasip3-http-macro" +version = "5.0.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a26dbd934e5451d21ef060c018dae56fc073894c5a7896f882928a76e6d081b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.107", +] + +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + +[[package]] +name = "unicode-ident" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06" + +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-properties" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "uuid" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.13.1+wasi-0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f43d1c36145feb89a3e61aa0ba3e582d976a8ab77f1474aa0adb80800fe0cf8" +dependencies = [ + "wit-bindgen-rt 0.24.0", +] + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen 0.46.0", +] + +[[package]] +name = "wasip3" +version = "0.2.1+wasi-0.3.0-rc-2025-09-16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbb2796323e2357ae2d4ba2b781a0392b533f40a5b9f534eef49b23e54186d64" +dependencies = [ + "bytes", + "http", + "http-body", + "thiserror", + "wit-bindgen 0.46.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.107", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.107", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.235.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3bc393c395cb621367ff02d854179882b9a351b4e0c93d1397e6090b53a5c2a" +dependencies = [ + "leb128fmt", + "wasmparser 0.235.0", +] + +[[package]] +name = "wasm-encoder" +version = "0.239.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be00faa2b4950c76fe618c409d2c3ea5a3c9422013e079482d78544bb2d184c" +dependencies = [ + "leb128fmt", + "wasmparser 0.239.0", +] + +[[package]] +name = "wasm-metadata" +version = "0.235.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b055604ba04189d54b8c0ab2c2fc98848f208e103882d5c0b984f045d5ea4d20" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder 0.235.0", + "wasmparser 0.235.0", +] + +[[package]] +name = "wasm-metadata" +version = "0.239.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20b3ec880a9ac69ccd92fbdbcf46ee833071cf09f82bb005b2327c7ae6025ae2" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder 0.239.0", + "wasmparser 0.239.0", +] + +[[package]] +name = "wasmparser" +version = "0.235.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "161296c618fa2d63f6ed5fffd1112937e803cb9ec71b32b01a76321555660917" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "wasmparser" +version = "0.239.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9d90bb93e764f6beabf1d02028c70a2156a6583e63ac4218dd07ef733368b0" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.107", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.107", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "wit-bindgen" +version = "0.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a18712ff1ec5bd09da500fe1e91dec11256b310da0ff33f8b4ec92b927cf0c6" +dependencies = [ + "wit-bindgen-rt 0.43.0", + "wit-bindgen-rust-macro 0.43.0", +] + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +dependencies = [ + "bitflags", + "futures", + "once_cell", + "wit-bindgen-rust-macro 0.46.0", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c53468e077362201de11999c85c07c36e12048a990a3e0d69da2bd61da355d0" +dependencies = [ + "anyhow", + "heck", + "wit-parser 0.235.0", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cabd629f94da277abc739c71353397046401518efb2c707669f805205f0b9890" +dependencies = [ + "anyhow", + "heck", + "wit-parser 0.239.0", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0780cf7046630ed70f689a098cd8d56c5c3b22f2a7379bbdb088879963ff96" +dependencies = [ + "bitflags", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fd734226eac1fd7c450956964e3a9094c9cee65e9dafdf126feef8c0096db65" +dependencies = [ + "bitflags", + "futures", + "once_cell", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "531ebfcec48e56473805285febdb450e270fa75b2dacb92816861d0473b4c15f" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn 2.0.107", + "wasm-metadata 0.235.0", + "wit-bindgen-core 0.43.0", + "wit-component 0.235.0", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a4232e841089fa5f3c4fc732a92e1c74e1a3958db3b12f1de5934da2027f1f4" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn 2.0.107", + "wasm-metadata 0.239.0", + "wit-bindgen-core 0.46.0", + "wit-component 0.239.0", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7852bf8a9d1ea80884d26b864ddebd7b0c7636697c6ca10f4c6c93945e023966" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.107", + "wit-bindgen-core 0.43.0", + "wit-bindgen-rust 0.43.0", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0d4698c2913d8d9c2b220d116409c3f51a7aa8d7765151b886918367179ee9" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.107", + "wit-bindgen-core 0.46.0", + "wit-bindgen-rust 0.46.0", +] + +[[package]] +name = "wit-component" +version = "0.235.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a57a11109cc553396f89f3a38a158a97d0b1adaec113bd73e0f64d30fb601f" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder 0.235.0", + "wasm-metadata 0.235.0", + "wasmparser 0.235.0", + "wit-parser 0.235.0", +] + +[[package]] +name = "wit-component" +version = "0.239.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a866b19dba2c94d706ec58c92a4c62ab63e482b4c935d2a085ac94caecb136" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder 0.239.0", + "wasm-metadata 0.239.0", + "wasmparser 0.239.0", + "wit-parser 0.239.0", +] + +[[package]] +name = "wit-parser" +version = "0.235.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a1f95a87d03a33e259af286b857a95911eb46236a0f726cbaec1227b3dfc67a" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser 0.235.0", +] + +[[package]] +name = "wit-parser" +version = "0.239.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55c92c939d667b7bf0c6bf2d1f67196529758f99a2a45a3355cc56964fd5315d" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser 0.239.0", +] + +[[package]] +name = "zerocopy" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.107", +] diff --git a/examples/wasip3-concurrent-outbound-http-calls/Cargo.toml b/examples/wasip3-concurrent-outbound-http-calls/Cargo.toml new file mode 100644 index 0000000..28bc248 --- /dev/null +++ b/examples/wasip3-concurrent-outbound-http-calls/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "concurrent-outbound-http-calls" +authors = ["itowlson "] +description = "" +version = "0.1.0" +rust-version = "1.90" # required for `wasm32-wasip2` target to build WASIp3 +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +anyhow = "1" +futures = "0.3.31" +http = "1.3" +spin-sdk = { path = "../..", features = ["wasip3-unstable"] } + +[workspace] diff --git a/examples/wasip3-concurrent-outbound-http-calls/spin.toml b/examples/wasip3-concurrent-outbound-http-calls/spin.toml new file mode 100644 index 0000000..b33beef --- /dev/null +++ b/examples/wasip3-concurrent-outbound-http-calls/spin.toml @@ -0,0 +1,21 @@ +#:schema https://schemas.spinframework.dev/spin/manifest-v2/latest.json + +spin_manifest_version = 2 + +[application] +name = "concurrent-outbound-http-calls" +version = "0.1.0" +authors = ["The Spin project"] +description = "Demonstrates making concurrent outbound HTTP calls in WASIp3" + +[[trigger.http]] +route = "/..." +component = "concurrent-outbound-http-calls" +executor = { type = "wasip3-unstable" } + +[component.concurrent-outbound-http-calls] +source = "target/wasm32-wasip2/release/concurrent_outbound_http_calls.wasm" +allowed_outbound_hosts = ["https://spinframework.dev", "https://component-model.bytecodealliance.org/"] +[component.concurrent-outbound-http-calls.build] +command = "cargo build --target wasm32-wasip2 --release" +watch = ["src/**/*.rs", "Cargo.toml"] diff --git a/examples/wasip3-concurrent-outbound-http-calls/src/lib.rs b/examples/wasip3-concurrent-outbound-http-calls/src/lib.rs new file mode 100644 index 0000000..46be3aa --- /dev/null +++ b/examples/wasip3-concurrent-outbound-http-calls/src/lib.rs @@ -0,0 +1,30 @@ +use std::pin::pin; + +use futures::future::Either; +use http::Request; +use spin_sdk::http_wasip3::{send, EmptyBody, IntoResponse}; +use spin_sdk::http_wasip3::http_service; + +#[http_service] +async fn handle_concurrent_outbound_http_calls(_req: spin_sdk::http_wasip3::Request) -> anyhow::Result { + + let spin = pin!(get_content_length("https://spinframework.dev")); + let book = pin!(get_content_length("https://component-model.bytecodealliance.org/")); + + let (first, len) = match futures::future::select(spin, book).await { + Either::Left(len) => ("Spin docs", len), + Either::Right(len) => ("Component model book", len), + }; + + let response = format!("{first} site was first response with content-length {:?}\n", len.0?); + + Ok(response) +} + +async fn get_content_length(url: &str) -> anyhow::Result> { + let request = Request::get(url).body(EmptyBody::new())?; + let response = send(request).await?; + let cl_header = response.headers().get("content-length"); + let cl = cl_header.and_then(|hval| hval.to_str().ok()).and_then(|hval| hval.parse().ok()); + Ok(cl) +} diff --git a/examples/wasip3-http-axum-router/.gitignore b/examples/wasip3-http-axum-router/.gitignore new file mode 100644 index 0000000..386474f --- /dev/null +++ b/examples/wasip3-http-axum-router/.gitignore @@ -0,0 +1,2 @@ +target/ +.spin/ diff --git a/examples/wasip3-http-axum-router/Cargo.toml b/examples/wasip3-http-axum-router/Cargo.toml new file mode 100644 index 0000000..e015e36 --- /dev/null +++ b/examples/wasip3-http-axum-router/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "axum-router" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +axum = { version = "0.8.1", default-features = false, features = ["json", "macros"] } +serde = { version = "1.0.163", features = ["derive"] } +spin-sdk = { path = "../..", features = ["wasip3-unstable"] } +tower-service = "0.3.3" \ No newline at end of file diff --git a/examples/wasip3-http-axum-router/README.md b/examples/wasip3-http-axum-router/README.md new file mode 100644 index 0000000..4477ac5 --- /dev/null +++ b/examples/wasip3-http-axum-router/README.md @@ -0,0 +1,9 @@ +# axum-router + +This example shows how to use [axum](https://github.com/tokio-rs/axum) with the `spin-sdk` + +``` +spin up --build +curl --json '{"username": "jiggs"}' localhost:3000/users +{"id":1337,"username":"jiggs"} +``` \ No newline at end of file diff --git a/examples/wasip3-http-axum-router/spin.toml b/examples/wasip3-http-axum-router/spin.toml new file mode 100644 index 0000000..aaa34c2 --- /dev/null +++ b/examples/wasip3-http-axum-router/spin.toml @@ -0,0 +1,21 @@ +#:schema https://schemas.spinframework.dev/spin/manifest-v2/latest.json + +spin_manifest_version = 2 + +[application] +name = "axum-router" +version = "0.1.0" +authors = ["Fermyon Engineering "] +description = "An example application using axum" + +[[trigger.http]] +route = "/..." +component = "axum-router" +executor = { type = "wasip3-unstable" } + +[component.axum-router] +source = "../../target/wasm32-wasip2/release/axum_router.wasm" +allowed_outbound_hosts = [] +[component.axum-router.build] +command = "cargo build --target wasm32-wasip2 --release" +watch = ["src/**/*.rs", "Cargo.toml"] diff --git a/examples/wasip3-http-axum-router/src/lib.rs b/examples/wasip3-http-axum-router/src/lib.rs new file mode 100644 index 0000000..869ca1e --- /dev/null +++ b/examples/wasip3-http-axum-router/src/lib.rs @@ -0,0 +1,51 @@ +use axum::{ + http::StatusCode, + routing::{get, post}, + Json, Router, +}; +use serde::{Deserialize, Serialize}; +use spin_sdk::http_wasip3::{http_service, IntoResponse, Request}; +use tower_service::Service; + +/// Demonstrates integration with the Axum web framework +#[http_service] +async fn handler(req: Request) -> impl IntoResponse { + Router::new() + .route("/", get(root)) + .route("/users", post(create_user)) + .call(req) + .await +} + +async fn root() -> &'static str { + "hello, world!" +} + +async fn create_user( + // this argument tells axum to parse the request body + // as JSON into a `CreateUser` type + Json(payload): Json, +) -> (StatusCode, Json) { + // insert your application logic here + let user = User { + id: 1337, + username: payload.username, + }; + + // this will be converted into a JSON response + // with a status code of `201 Created` + (StatusCode::CREATED, Json(user)) +} + +// the input to our `create_user` handler +#[derive(Deserialize)] +struct CreateUser { + username: String, +} + +// the output to our `create_user` handler +#[derive(Serialize)] +struct User { + id: u64, + username: String, +} diff --git a/examples/wasip3-http-hello-world/Cargo.toml b/examples/wasip3-http-hello-world/Cargo.toml new file mode 100644 index 0000000..b091358 --- /dev/null +++ b/examples/wasip3-http-hello-world/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "wasip3-hello-world" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +spin-sdk = { path = "../..", features = ["wasip3-unstable"] } \ No newline at end of file diff --git a/examples/wasip3-http-hello-world/spin.toml b/examples/wasip3-http-hello-world/spin.toml new file mode 100644 index 0000000..67daf8d --- /dev/null +++ b/examples/wasip3-http-hello-world/spin.toml @@ -0,0 +1,21 @@ +#:schema https://schemas.spinframework.dev/spin/manifest-v2/latest.json + +spin_manifest_version = 2 + +[application] +authors = ["Fermyon Engineering "] +description = "An application that returns hello." +name = "hello-world" +version = "1.0.0" + +[[trigger.http]] +route = "/hello" +component = "hello" +executor = { type = "wasip3-unstable" } + +[component.hello] +source = "../../target/wasm32-wasip2/release/wasip3_hello_world.wasm" +description = "A component that returns hello." +[component.hello.build] +command = "cargo build --target wasm32-wasip2 --release" +watch = ["src/**/*.rs", "Cargo.toml"] \ No newline at end of file diff --git a/examples/wasip3-http-hello-world/src/lib.rs b/examples/wasip3-http-hello-world/src/lib.rs new file mode 100644 index 0000000..e49e10e --- /dev/null +++ b/examples/wasip3-http-hello-world/src/lib.rs @@ -0,0 +1,7 @@ +use spin_sdk::http_wasip3::{http_service, Request}; + +/// A simple Spin HTTP component. +#[http_service] +async fn hello_world(_req: Request) -> &'static str { + "Hello, world!" +} diff --git a/examples/wasip3-http-send-request/Cargo.toml b/examples/wasip3-http-send-request/Cargo.toml new file mode 100644 index 0000000..142f6c3 --- /dev/null +++ b/examples/wasip3-http-send-request/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "send-request" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +anyhow = "1" +axum = { version = "0.8.1", default-features = false, features = ["macros"] } +http = "1.3.1" +spin-sdk = { path = "../..", features = ["wasip3-unstable"] } \ No newline at end of file diff --git a/examples/wasip3-http-send-request/spin.toml b/examples/wasip3-http-send-request/spin.toml new file mode 100644 index 0000000..e9b3810 --- /dev/null +++ b/examples/wasip3-http-send-request/spin.toml @@ -0,0 +1,24 @@ +#:schema https://schemas.spinframework.dev/spin/manifest-v2/latest.json + +spin_manifest_version = 2 + +[application] +authors = ["Fermyon Engineering "] +description = "An application that sends an HTTP request" +name = "send-request" +version = "1.0.0" + +[[trigger.http]] +route = "/..." +component = "send" +executor = { type = "wasip3-unstable" } + +[component.send] +source = "../../target/wasm32-wasip2/release/send_request.wasm" +description = "A component that sends a request." +allowed_outbound_hosts = [ + "https://bytecodealliance.org", +] +[component.send.build] +command = "cargo build --target wasm32-wasip2 --release" +watch = ["src/**/*.rs", "Cargo.toml"] \ No newline at end of file diff --git a/examples/wasip3-http-send-request/src/lib.rs b/examples/wasip3-http-send-request/src/lib.rs new file mode 100644 index 0000000..79cf9bb --- /dev/null +++ b/examples/wasip3-http-send-request/src/lib.rs @@ -0,0 +1,9 @@ +use spin_sdk::http_wasip3::{http_service, send, EmptyBody, IntoResponse, Request, Result}; + +/// Sends a request to a URL. +#[http_service] +async fn send_request(_req: Request) -> Result { + let outgoing = http::Request::get("https://bytecodealliance.org").body(EmptyBody::new())?; + + Ok(send(outgoing).await?) +} diff --git a/src/lib.rs b/src/lib.rs index 53e115a..1443c4c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,16 @@ pub mod llm; pub use spin_macro::*; +/// WASIp3 HTTP APIs and helpers +#[cfg(feature = "wasip3-unstable")] +pub mod http_wasip3 { + /// Re-exports the helpers types for converting between WASIp3 HTTP types and + /// Rust ecosystem HTTP types. + pub use spin_wasip3_http::*; + /// Re-exports the macro to enable WASIp3 HTTP handlers + pub use spin_wasip3_http_macro::*; +} + #[doc(hidden)] /// Module containing wit bindgen generated code. ///