From d96994767c92a1fe4e802359508ab4a20eddfc9e Mon Sep 17 00:00:00 2001 From: 007gzs <007gzs@gmail.com> Date: Mon, 21 Oct 2024 09:44:01 +0800 Subject: [PATCH 1/4] Change http_content to Rc in HttpWrapper (#1391) --- plugins/wasm-rust/Cargo.lock | 50 +++-- plugins/wasm-rust/Cargo.toml | 1 + .../extensions/ai-data-masking/Cargo.lock | 137 +++++++------ .../extensions/ai-data-masking/src/lib.rs | 4 +- .../wasm-rust/extensions/demo-wasm/Cargo.lock | 50 +++-- .../wasm-rust/extensions/demo-wasm/src/lib.rs | 109 +++++------ .../extensions/request-block/Cargo.lock | 93 ++++++--- .../extensions/request-block/src/lib.rs | 8 +- .../wasm-rust/extensions/say-hello/Cargo.lock | 81 ++++++-- plugins/wasm-rust/src/plugin_wrapper.rs | 184 ++++++++++-------- plugins/wasm-rust/src/rule_matcher.rs | 25 +-- 11 files changed, 442 insertions(+), 300 deletions(-) diff --git a/plugins/wasm-rust/Cargo.lock b/plugins/wasm-rust/Cargo.lock index 899d559b0d..9cf5140c77 100644 --- a/plugins/wasm-rust/Cargo.lock +++ b/plugins/wasm-rust/Cargo.lock @@ -32,6 +32,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + [[package]] name = "fnv" version = "1.0.7" @@ -63,6 +69,7 @@ dependencies = [ name = "higress-wasm-rust" version = "0.1.0" dependencies = [ + "downcast-rs", "http", "lazy_static", "multimap", @@ -97,9 +104,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "log" @@ -124,15 +131,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" dependencies = [ "unicode-ident", ] @@ -140,17 +147,18 @@ dependencies = [ [[package]] name = "proxy-wasm" version = "0.2.2" -source = "git+https://github.com/higress-group/proxy-wasm-rust-sdk?branch=main#73833051f57d483570cf5aaa9d62bd7402fae63b" +source = "git+https://github.com/higress-group/proxy-wasm-rust-sdk?branch=main#6735737fad486c8a7cc324241f58df4a160e7887" dependencies = [ + "downcast-rs", "hashbrown", "log", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -163,18 +171,18 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "serde" -version = "1.0.207" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5665e14a49a4ea1b91029ba7d3bca9f299e1f7cfa194388ccc20f14743e784f2" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.207" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aea2634c86b0e8ef2cfdc0c340baede54ec27b1e46febd7f80dffb2aa44a00e" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -183,9 +191,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.125" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", "memchr", @@ -195,9 +203,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.74" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -206,15 +214,15 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "uuid" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" dependencies = [ "getrandom", ] diff --git a/plugins/wasm-rust/Cargo.toml b/plugins/wasm-rust/Cargo.toml index a1e5472c63..e4fc274311 100644 --- a/plugins/wasm-rust/Cargo.toml +++ b/plugins/wasm-rust/Cargo.toml @@ -13,3 +13,4 @@ uuid = { version = "1.3.3", features = ["v4"] } multimap = "0" http = "1" lazy_static = "1" +downcast-rs="1" diff --git a/plugins/wasm-rust/extensions/ai-data-masking/Cargo.lock b/plugins/wasm-rust/extensions/ai-data-masking/Cargo.lock index 853fde94fb..914d5b9689 100644 --- a/plugins/wasm-rust/extensions/ai-data-masking/Cargo.lock +++ b/plugins/wasm-rust/extensions/ai-data-masking/Cargo.lock @@ -82,11 +82,17 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "bytes" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" + [[package]] name = "cc" -version = "1.1.11" +version = "1.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fb8dd288a69fc53a1996d7ecfbf4a20d59065bff137ce7e56bbd620de191189" +checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" dependencies = [ "shlex", ] @@ -108,9 +114,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cpufeatures" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -162,18 +168,18 @@ dependencies = [ [[package]] name = "derive_builder" -version = "0.20.0" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0350b5cb0331628a5916d6c5c0b72e97393b8b6b03b47a9284f4e7f5a405ffd7" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" dependencies = [ "derive_builder_macro", ] [[package]] name = "derive_builder_core" -version = "0.20.0" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d48cda787f839151732d396ac69e3473923d54312c070ee21e9effcaa8ca0b1d" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" dependencies = [ "darling", "proc-macro2", @@ -183,9 +189,9 @@ dependencies = [ [[package]] name = "derive_builder_macro" -version = "0.20.0" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", "syn", @@ -201,6 +207,12 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + [[package]] name = "fancy-regex" version = "0.13.0" @@ -278,6 +290,9 @@ dependencies = [ name = "higress-wasm-rust" version = "0.1.0" dependencies = [ + "downcast-rs", + "http", + "lazy_static", "multimap", "proxy-wasm", "serde", @@ -285,6 +300,17 @@ dependencies = [ "uuid", ] +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -314,9 +340,9 @@ dependencies = [ [[package]] name = "jsonpath-rust" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d64f9886fc067a709ab27faf63b7d3f4d1ec570a700705408b0b0683e2f43897" +checksum = "514f8a353ad9e85443b30fefe169ce93794ec7c98054a4312ab08530f15b7bb3" dependencies = [ "pest", "pest_derive", @@ -333,9 +359,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "log" @@ -366,9 +392,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "onig" @@ -394,9 +420,9 @@ dependencies = [ [[package]] name = "pest" -version = "2.7.11" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" +checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" dependencies = [ "memchr", "thiserror", @@ -405,9 +431,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.11" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a" +checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd" dependencies = [ "pest", "pest_generator", @@ -415,9 +441,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.11" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183" +checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e" dependencies = [ "pest", "pest_meta", @@ -428,9 +454,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.7.11" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f" +checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d" dependencies = [ "once_cell", "pest", @@ -477,15 +503,15 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" dependencies = [ "unicode-ident", ] @@ -493,17 +519,18 @@ dependencies = [ [[package]] name = "proxy-wasm" version = "0.2.2" -source = "git+https://github.com/higress-group/proxy-wasm-rust-sdk?branch=main#73833051f57d483570cf5aaa9d62bd7402fae63b" +source = "git+https://github.com/higress-group/proxy-wasm-rust-sdk?branch=main#6735737fad486c8a7cc324241f58df4a160e7887" dependencies = [ + "downcast-rs", "hashbrown", "log", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -525,9 +552,9 @@ checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" [[package]] name = "regex" -version = "1.10.6" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", @@ -537,9 +564,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", @@ -548,9 +575,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rust-embed" @@ -603,18 +630,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.207" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5665e14a49a4ea1b91029ba7d3bca9f299e1f7cfa194388ccc20f14743e784f2" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.207" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aea2634c86b0e8ef2cfdc0c340baede54ec27b1e46febd7f80dffb2aa44a00e" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -623,9 +650,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.125" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", "memchr", @@ -670,9 +697,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.74" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -681,18 +708,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", @@ -707,21 +734,21 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "uuid" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" dependencies = [ "getrandom", ] diff --git a/plugins/wasm-rust/extensions/ai-data-masking/src/lib.rs b/plugins/wasm-rust/extensions/ai-data-masking/src/lib.rs index a6114bdd8b..01573585ac 100644 --- a/plugins/wasm-rust/extensions/ai-data-masking/src/lib.rs +++ b/plugins/wasm-rust/extensions/ai-data-masking/src/lib.rs @@ -65,7 +65,7 @@ struct AiDataMaskingRoot { rule_matcher: SharedRuleMatcher, } struct AiDataMasking { - config: Option, + config: Option>, mask_map: HashMap>, is_openai: bool, stream: bool, @@ -585,7 +585,7 @@ impl HttpContext for AiDataMasking { } } impl HttpContextWrapper for AiDataMasking { - fn on_config(&mut self, config: &AiDataMaskingConfig) { + fn on_config(&mut self, config: Rc) { self.config = Some(config.clone()); } fn cache_request_body(&self) -> bool { diff --git a/plugins/wasm-rust/extensions/demo-wasm/Cargo.lock b/plugins/wasm-rust/extensions/demo-wasm/Cargo.lock index 85e2edaea3..c1e197caf6 100644 --- a/plugins/wasm-rust/extensions/demo-wasm/Cargo.lock +++ b/plugins/wasm-rust/extensions/demo-wasm/Cargo.lock @@ -43,6 +43,12 @@ dependencies = [ "serde", ] +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + [[package]] name = "fnv" version = "1.0.7" @@ -74,6 +80,7 @@ dependencies = [ name = "higress-wasm-rust" version = "0.1.0" dependencies = [ + "downcast-rs", "http", "lazy_static", "multimap", @@ -108,9 +115,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.157" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374af5f94e54fa97cf75e945cce8a6b201e88a1a07e688b47dfd2a59c66dbd86" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "log" @@ -135,15 +142,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" dependencies = [ "unicode-ident", ] @@ -151,17 +158,18 @@ dependencies = [ [[package]] name = "proxy-wasm" version = "0.2.2" -source = "git+https://github.com/higress-group/proxy-wasm-rust-sdk?branch=main#73833051f57d483570cf5aaa9d62bd7402fae63b" +source = "git+https://github.com/higress-group/proxy-wasm-rust-sdk?branch=main#6735737fad486c8a7cc324241f58df4a160e7887" dependencies = [ + "downcast-rs", "hashbrown", "log", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -174,18 +182,18 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "serde" -version = "1.0.208" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.208" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -194,9 +202,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.125" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", "memchr", @@ -206,9 +214,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.75" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -217,15 +225,15 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "uuid" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" dependencies = [ "getrandom", ] diff --git a/plugins/wasm-rust/extensions/demo-wasm/src/lib.rs b/plugins/wasm-rust/extensions/demo-wasm/src/lib.rs index 55647a83cc..62dfa055d0 100644 --- a/plugins/wasm-rust/extensions/demo-wasm/src/lib.rs +++ b/plugins/wasm-rust/extensions/demo-wasm/src/lib.rs @@ -1,8 +1,6 @@ use higress_wasm_rust::cluster_wrapper::DnsCluster; use higress_wasm_rust::log::Log; -use higress_wasm_rust::plugin_wrapper::{ - HttpCallArgStorage, HttpCallbackFn, HttpContextWrapper, RootContextWrapper, -}; +use higress_wasm_rust::plugin_wrapper::{HttpContextWrapper, RootContextWrapper}; use higress_wasm_rust::rule_matcher::{on_configure, RuleMatcher, SharedRuleMatcher}; use http::Method; use multimap::MultiMap; @@ -12,7 +10,7 @@ use proxy_wasm::types::{Bytes, ContextType, DataAction, HeaderAction, LogLevel}; use serde::Deserialize; use std::cell::RefCell; use std::ops::DerefMut; -use std::rc::Rc; +use std::rc::{Rc, Weak}; use std::time::Duration; proxy_wasm::main! {{ @@ -37,40 +35,27 @@ fn format_body(body: Option>) -> String { format!("{:?}", body) } -fn test_callback( - this: &mut DemoWasm, - status_code: u16, - headers: &MultiMap, - body: Option>, -) { - this.log.info(&format!( - "test_callback status_code:{}, headers: {:?}, body: {}", - status_code, - headers, - format_body(body) - )); - this.reset_http_request(); -} struct DemoWasm { // 每个请求对应的插件实例 log: Log, - config: Option, - - arg_storage: HttpCallArgStorage>>, + config: Option>, + weak: Weak>>>, } impl Context for DemoWasm {} impl HttpContext for DemoWasm {} -impl HttpContextWrapper>> for DemoWasm { +impl HttpContextWrapper for DemoWasm { + fn init_self_weak( + &mut self, + self_weak: Weak>>>, + ) { + self.weak = self_weak; + self.log.info("init_self_rc"); + } fn log(&self) -> &Log { &self.log } - fn get_http_call_storage( - &mut self, - ) -> Option<&mut HttpCallArgStorage>>> { - Some(&mut self.arg_storage) - } - fn on_config(&mut self, config: &DemoWasmConfig) { + fn on_config(&mut self, config: Rc) { // 获取config self.log.info(&format!("on_config {}", config.test)); self.config = Some(config.clone()) @@ -101,16 +86,6 @@ impl HttpContextWrapper>> for DemoW // 是否缓存返回body true } - fn on_http_call_response_detail( - &mut self, - _token_id: u32, - arg: Box>, - status_code: u16, - headers: &MultiMap, - body: Option>, - ) { - arg(self, status_code, headers, body) - } fn on_http_request_complete_body(&mut self, req_body: &Bytes) -> DataAction { // 请求body获取完成回调 self.log.info(&format!( @@ -118,23 +93,41 @@ impl HttpContextWrapper>> for DemoW String::from_utf8(req_body.clone()).unwrap_or("".to_string()) )); let cluster = DnsCluster::new("httpbin", "httpbin.org", 80); - if self - .http_call( - &cluster, - &Method::POST, - "http://httpbin.org/post", - MultiMap::new(), - Some("test_body".as_bytes()), - // Box::new(move |this, _status_code, _headers, _body| this.resume_http_request()), - Box::new(test_callback), - Duration::from_secs(5), - ) - .is_ok() - { - DataAction::StopIterationAndBuffer - } else { - self.log.info("http_call fail"); - DataAction::Continue + + let self_rc = match self.weak.upgrade() { + Some(rc) => rc.clone(), + None => { + self.log.error("self_weak upgrade error"); + return DataAction::Continue; + } + }; + let http_call_res = self.http_call( + &cluster, + &Method::POST, + "http://httpbin.org/post", + MultiMap::new(), + Some("test_body".as_bytes()), + Box::new(move |status_code, headers, body| { + if let Some(this) = self_rc.borrow().downcast_ref::() { + this.log.info(&format!( + "test_callback status_code:{}, headers: {:?}, body: {}", + status_code, + headers, + format_body(body) + )); + this.resume_http_request(); + } else { + self_rc.borrow().resume_http_request(); + } + }), + Duration::from_secs(5), + ); + match http_call_res { + Ok(_) => DataAction::StopIterationAndBuffer, + Err(e) => { + self.log.info(&format!("http_call fail {:?}", e)); + DataAction::Continue + } } } fn on_http_response_complete_body(&mut self, res_body: &Bytes) -> DataAction { @@ -185,7 +178,7 @@ impl RootContext for DemoWasmRoot { } } -impl RootContextWrapper>> for DemoWasmRoot { +impl RootContextWrapper for DemoWasmRoot { fn rule_matcher(&self) -> &SharedRuleMatcher { &self.rule_matcher } @@ -193,11 +186,11 @@ impl RootContextWrapper>> for DemoW fn create_http_context_wrapper( &self, _context_id: u32, - ) -> Option>>>> { + ) -> Option>> { Some(Box::new(DemoWasm { config: None, log: Log::new(PLUGIN_NAME.to_string()), - arg_storage: HttpCallArgStorage::new(), + weak: Weak::default(), })) } } diff --git a/plugins/wasm-rust/extensions/request-block/Cargo.lock b/plugins/wasm-rust/extensions/request-block/Cargo.lock index acc9caeb26..243fe87a49 100644 --- a/plugins/wasm-rust/extensions/request-block/Cargo.lock +++ b/plugins/wasm-rust/extensions/request-block/Cargo.lock @@ -29,12 +29,30 @@ version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +[[package]] +name = "bytes" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "getrandom" version = "0.2.15" @@ -60,6 +78,9 @@ dependencies = [ name = "higress-wasm-rust" version = "0.1.0" dependencies = [ + "downcast-rs", + "http", + "lazy_static", "multimap", "proxy-wasm", "serde", @@ -67,17 +88,34 @@ dependencies = [ "uuid", ] +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" -version = "0.2.155" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "log" @@ -102,15 +140,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" dependencies = [ "unicode-ident", ] @@ -118,26 +156,27 @@ dependencies = [ [[package]] name = "proxy-wasm" version = "0.2.2" -source = "git+https://github.com/higress-group/proxy-wasm-rust-sdk?branch=main#73833051f57d483570cf5aaa9d62bd7402fae63b" +source = "git+https://github.com/higress-group/proxy-wasm-rust-sdk?branch=main#6735737fad486c8a7cc324241f58df4a160e7887" dependencies = [ + "downcast-rs", "hashbrown", "log", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] [[package]] name = "regex" -version = "1.10.6" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", @@ -147,9 +186,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", @@ -158,9 +197,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "request-block" @@ -182,18 +221,18 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "serde" -version = "1.0.207" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5665e14a49a4ea1b91029ba7d3bca9f299e1f7cfa194388ccc20f14743e784f2" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.207" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aea2634c86b0e8ef2cfdc0c340baede54ec27b1e46febd7f80dffb2aa44a00e" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -202,9 +241,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.125" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", "memchr", @@ -214,9 +253,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.74" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -225,15 +264,15 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "uuid" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" dependencies = [ "getrandom", ] diff --git a/plugins/wasm-rust/extensions/request-block/src/lib.rs b/plugins/wasm-rust/extensions/request-block/src/lib.rs index 38d50de187..d120acb902 100644 --- a/plugins/wasm-rust/extensions/request-block/src/lib.rs +++ b/plugins/wasm-rust/extensions/request-block/src/lib.rs @@ -41,7 +41,7 @@ struct RquestBlockRoot { struct RquestBlock { log: Log, - config: Option, + config: Option>, cache_request: bool, } @@ -141,9 +141,9 @@ impl RootContextWrapper for RquestBlockRoot { impl Context for RquestBlock {} impl HttpContext for RquestBlock {} impl HttpContextWrapper for RquestBlock { - fn on_config(&mut self, _config: &RquestBlockConfig) { - self.config = Some(_config.clone()); - self.cache_request = !_config.block_bodies.is_empty(); + fn on_config(&mut self, config: Rc) { + self.cache_request = !config.block_bodies.is_empty(); + self.config = Some(config.clone()); } fn cache_request_body(&self) -> bool { self.cache_request diff --git a/plugins/wasm-rust/extensions/say-hello/Cargo.lock b/plugins/wasm-rust/extensions/say-hello/Cargo.lock index 5a50473fcd..dc98fd709a 100644 --- a/plugins/wasm-rust/extensions/say-hello/Cargo.lock +++ b/plugins/wasm-rust/extensions/say-hello/Cargo.lock @@ -20,12 +20,30 @@ version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +[[package]] +name = "bytes" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "getrandom" version = "0.2.15" @@ -51,6 +69,9 @@ dependencies = [ name = "higress-wasm-rust" version = "0.1.0" dependencies = [ + "downcast-rs", + "http", + "lazy_static", "multimap", "proxy-wasm", "serde", @@ -58,17 +79,34 @@ dependencies = [ "uuid", ] +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" -version = "0.2.155" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "log" @@ -93,15 +131,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" dependencies = [ "unicode-ident", ] @@ -109,17 +147,18 @@ dependencies = [ [[package]] name = "proxy-wasm" version = "0.2.2" -source = "git+https://github.com/higress-group/proxy-wasm-rust-sdk?branch=main#73833051f57d483570cf5aaa9d62bd7402fae63b" +source = "git+https://github.com/higress-group/proxy-wasm-rust-sdk?branch=main#6735737fad486c8a7cc324241f58df4a160e7887" dependencies = [ + "downcast-rs", "hashbrown", "log", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -142,18 +181,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.207" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5665e14a49a4ea1b91029ba7d3bca9f299e1f7cfa194388ccc20f14743e784f2" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.207" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aea2634c86b0e8ef2cfdc0c340baede54ec27b1e46febd7f80dffb2aa44a00e" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -162,9 +201,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.125" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", "memchr", @@ -174,9 +213,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.74" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -185,15 +224,15 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "uuid" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" dependencies = [ "getrandom", ] diff --git a/plugins/wasm-rust/src/plugin_wrapper.rs b/plugins/wasm-rust/src/plugin_wrapper.rs index 25d445f22f..5a00c0bda4 100644 --- a/plugins/wasm-rust/src/plugin_wrapper.rs +++ b/plugins/wasm-rust/src/plugin_wrapper.rs @@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::cell::RefCell; use std::collections::HashMap; +use std::rc::{Rc, Weak}; use std::time::Duration; use crate::cluster_wrapper::Cluster; @@ -28,10 +30,13 @@ use serde::de::DeserializeOwned; lazy_static! { static ref LOG: Log = Log::new("plugin_wrapper".to_string()); } +thread_local! { + static HTTP_CALLBACK_DISPATCHER: HttpCallbackDispatcher = HttpCallbackDispatcher::new(); +} -pub trait RootContextWrapper: RootContext +pub trait RootContextWrapper: RootContext where - PluginConfig: Default + DeserializeOwned + 'static + Clone, + PluginConfig: Default + DeserializeOwned + Clone + 'static, { // fn create_http_context(&self, context_id: u32) -> Option> { fn create_http_context_use_wrapper(&self, context_id: u32) -> Option> { @@ -48,38 +53,48 @@ where fn create_http_context_wrapper( &self, _context_id: u32, - ) -> Option>> { + ) -> Option>> { None } } -pub type HttpCallbackFn = dyn FnOnce(&mut T, u16, &MultiMap, Option>); -pub struct HttpCallArgStorage { - args: HashMap, +pub type HttpCallbackFn = dyn FnOnce(u16, &MultiMap, Option>); + +pub struct HttpCallbackDispatcher { + call_fns: RefCell>>, } -impl Default for HttpCallArgStorage { +impl Default for HttpCallbackDispatcher { fn default() -> Self { Self::new() } } -impl HttpCallArgStorage { +impl HttpCallbackDispatcher { pub fn new() -> Self { - HttpCallArgStorage { - args: HashMap::new(), + HttpCallbackDispatcher { + call_fns: RefCell::new(HashMap::new()), } } - pub fn set(&mut self, token_id: u32, arg: HttpCallArg) { - self.args.insert(token_id, arg); + pub fn set(&self, token_id: u32, arg: Box) { + self.call_fns.borrow_mut().insert(token_id, arg); } - pub fn pop(&mut self, token_id: u32) -> Option { - self.args.remove(&token_id) + pub fn pop(&self, token_id: u32) -> Option> { + self.call_fns.borrow_mut().remove(&token_id) } } -pub trait HttpContextWrapper: HttpContext { + +pub trait HttpContextWrapper: HttpContext +where + PluginConfig: Default + DeserializeOwned + Clone + 'static, +{ + fn init_self_weak( + &mut self, + _self_weak: Weak>>>, + ) { + } fn log(&self) -> &Log { &LOG } - fn on_config(&mut self, _config: &PluginConfig) {} + fn on_config(&mut self, _config: Rc) {} fn on_http_request_complete_headers( &mut self, _headers: &MultiMap, @@ -105,16 +120,6 @@ pub trait HttpContextWrapper: HttpContext { DataAction::Continue } - #[allow(clippy::too_many_arguments)] - fn on_http_call_response_detail( - &mut self, - _token_id: u32, - _arg: HttpCallArg, - _status_code: u16, - _headers: &MultiMap, - _body: Option>, - ) { - } fn replace_http_request_body(&mut self, body: &[u8]) { self.set_http_request_body(0, i32::MAX as usize, body) } @@ -122,10 +127,6 @@ pub trait HttpContextWrapper: HttpContext { self.set_http_response_body(0, i32::MAX as usize, body) } - fn get_http_call_storage(&mut self) -> Option<&mut HttpCallArgStorage> { - None - } - #[allow(clippy::too_many_arguments)] fn http_call( &mut self, @@ -134,7 +135,7 @@ pub trait HttpContextWrapper: HttpContext { raw_url: &str, headers: MultiMap, body: Option<&[u8]>, - arg: HttpCallArg, + call_fn: Box, timeout: Duration, ) -> Result { if let Ok(uri) = raw_url.parse::() { @@ -162,17 +163,13 @@ pub trait HttpContextWrapper: HttpContext { ); if let Ok(token_id) = ret { - if let Some(storage) = self.get_http_call_storage() { - storage.set(token_id, arg); - self.log().debug( - &format!( - "http call start, id: {}, cluster: {}, method: {}, url: {}, body: {:?}, timeout: {:?}", - token_id, cluster.cluster_name(), method.as_str(), raw_url, body, timeout - ) - ); - } else { - return Err(Status::InternalFailure); - } + HTTP_CALLBACK_DISPATCHER.with(|dispatcher| dispatcher.set(token_id, call_fn)); + self.log().debug( + &format!( + "http call start, id: {}, cluster: {}, method: {}, url: {}, body: {:?}, timeout: {:?}", + token_id, cluster.cluster_name(), method.as_str(), raw_url, body, timeout + ) + ); } ret } else { @@ -181,20 +178,30 @@ pub trait HttpContextWrapper: HttpContext { } } } -pub struct PluginHttpWrapper { + +downcast_rs::impl_downcast!(HttpContextWrapper where PluginConfig: Default + DeserializeOwned + Clone); + +pub struct PluginHttpWrapper { req_headers: MultiMap, res_headers: MultiMap, req_body_len: usize, res_body_len: usize, - config: Option, + config: Option>, rule_matcher: SharedRuleMatcher, - http_content: Box>, + http_content: Rc>>>, } -impl PluginHttpWrapper { +impl PluginHttpWrapper +where + PluginConfig: Default + DeserializeOwned + Clone + 'static, +{ pub fn new( rule_matcher: &SharedRuleMatcher, - http_content: Box>, + http_content: Box>, ) -> Self { + let rc_content = Rc::new(RefCell::new(http_content)); + rc_content + .borrow_mut() + .init_self_weak(Rc::downgrade(&rc_content)); PluginHttpWrapper { req_headers: MultiMap::new(), res_headers: MultiMap::new(), @@ -202,18 +209,17 @@ impl PluginHttpWrapper { res_body_len: 0, config: None, rule_matcher: rule_matcher.clone(), - http_content, + http_content: rc_content, } } - fn get_http_call_arg(&mut self, token_id: u32) -> Option { - if let Some(storage) = self.http_content.get_http_call_storage() { - storage.pop(token_id) - } else { - None - } + fn get_http_call_fn(&mut self, token_id: u32) -> Option> { + HTTP_CALLBACK_DISPATCHER.with(|dispatcher| dispatcher.pop(token_id)) } } -impl Context for PluginHttpWrapper { +impl Context for PluginHttpWrapper +where + PluginConfig: Default + DeserializeOwned + Clone + 'static, +{ fn on_http_call_response( &mut self, token_id: u32, @@ -221,7 +227,7 @@ impl Context for PluginHttpWrapper Context for PluginHttpWrapper Context for PluginHttpWrapper { - self.http_content.log().warn(&format!( + self.http_content.borrow().log().warn(&format!( "http call response header contains non-ASCII characters header: {}", k )); } } } - self.http_content.log().warn(&format!( - "http call end, id: {}, code: {}, normal: {}, body: {:?}", + self.http_content.borrow().log().warn(&format!( + "http call end, id: {}, code: {}, normal: {}, body: {:?}", /* */ token_id, status_code, normal_response, body )); - self.http_content.on_http_call_response_detail( + call_fn(status_code, &headers, body) + } else { + self.http_content.borrow_mut().on_http_call_response( token_id, - arg, - status_code, - &headers, - body, + num_headers, + body_size, + num_trailers, ) - } else { - self.http_content - .on_http_call_response(token_id, num_headers, body_size, num_trailers) } } fn on_grpc_call_response(&mut self, token_id: u32, status_code: u32, response_size: usize) { self.http_content + .borrow_mut() .on_grpc_call_response(token_id, status_code, response_size) } fn on_grpc_stream_initial_metadata(&mut self, token_id: u32, num_elements: u32) { self.http_content + .borrow_mut() .on_grpc_stream_initial_metadata(token_id, num_elements) } fn on_grpc_stream_message(&mut self, token_id: u32, message_size: usize) { self.http_content + .borrow_mut() .on_grpc_stream_message(token_id, message_size) } fn on_grpc_stream_trailing_metadata(&mut self, token_id: u32, num_elements: u32) { self.http_content + .borrow_mut() .on_grpc_stream_trailing_metadata(token_id, num_elements) } fn on_grpc_stream_close(&mut self, token_id: u32, status_code: u32) { self.http_content + .borrow_mut() .on_grpc_stream_close(token_id, status_code) } fn on_done(&mut self) -> bool { - self.http_content.on_done() + self.http_content.borrow_mut().on_done() } } -impl HttpContext for PluginHttpWrapper +impl HttpContext for PluginHttpWrapper where - PluginConfig: Default + DeserializeOwned + Clone, + PluginConfig: Default + DeserializeOwned + Clone + 'static, { fn on_http_request_headers(&mut self, num_headers: usize, end_of_stream: bool) -> HeaderAction { let binding = self.rule_matcher.borrow(); @@ -306,7 +316,7 @@ where self.req_headers.insert(k, header_value); } Err(_) => { - self.http_content.log().warn(&format!( + self.http_content.borrow().log().warn(&format!( "request http header contains non-ASCII characters header: {}", k )); @@ -315,22 +325,25 @@ where } if let Some(config) = &self.config { - self.http_content.on_config(config); + self.http_content.borrow_mut().on_config(config.clone()); } let ret = self .http_content + .borrow_mut() .on_http_request_headers(num_headers, end_of_stream); if ret != HeaderAction::Continue { return ret; } self.http_content + .borrow_mut() .on_http_request_complete_headers(&self.req_headers) } fn on_http_request_body(&mut self, body_size: usize, end_of_stream: bool) -> DataAction { - if !self.http_content.cache_request_body() { + if !self.http_content.borrow().cache_request_body() { return self .http_content + .borrow_mut() .on_http_request_body(body_size, end_of_stream); } self.req_body_len += body_size; @@ -343,11 +356,15 @@ where req_body = body; } } - self.http_content.on_http_request_complete_body(&req_body) + self.http_content + .borrow_mut() + .on_http_request_complete_body(&req_body) } fn on_http_request_trailers(&mut self, num_trailers: usize) -> Action { - self.http_content.on_http_request_trailers(num_trailers) + self.http_content + .borrow_mut() + .on_http_request_trailers(num_trailers) } fn on_http_response_headers( @@ -361,7 +378,7 @@ where self.res_headers.insert(k, header_value); } Err(_) => { - self.http_content.log().warn(&format!( + self.http_content.borrow().log().warn(&format!( "response http header contains non-ASCII characters header: {}", k )); @@ -371,18 +388,21 @@ where let ret = self .http_content + .borrow_mut() .on_http_response_headers(num_headers, end_of_stream); if ret != HeaderAction::Continue { return ret; } self.http_content + .borrow_mut() .on_http_response_complete_headers(&self.res_headers) } fn on_http_response_body(&mut self, body_size: usize, end_of_stream: bool) -> DataAction { - if !self.http_content.cache_response_body() { + if !self.http_content.borrow().cache_response_body() { return self .http_content + .borrow_mut() .on_http_response_body(body_size, end_of_stream); } self.res_body_len += body_size; @@ -397,14 +417,18 @@ where res_body = body; } } - self.http_content.on_http_response_complete_body(&res_body) + self.http_content + .borrow_mut() + .on_http_response_complete_body(&res_body) } fn on_http_response_trailers(&mut self, num_trailers: usize) -> Action { - self.http_content.on_http_response_trailers(num_trailers) + self.http_content + .borrow_mut() + .on_http_response_trailers(num_trailers) } fn on_log(&mut self) { - self.http_content.on_log() + self.http_content.borrow_mut().on_log() } } diff --git a/plugins/wasm-rust/src/rule_matcher.rs b/plugins/wasm-rust/src/rule_matcher.rs index 6478344334..dc42b433fc 100644 --- a/plugins/wasm-rust/src/rule_matcher.rs +++ b/plugins/wasm-rust/src/rule_matcher.rs @@ -20,6 +20,7 @@ use proxy_wasm::traits::RootContext; use proxy_wasm::types::LogLevel; use serde::de::DeserializeOwned; use serde_json::{from_slice, Map, Value}; +use std::borrow::Borrow; use std::cell::RefCell; use std::collections::HashSet; use std::rc::Rc; @@ -50,13 +51,13 @@ struct RuleConfig { category: Category, routes: HashSet, hosts: Vec, - config: PluginConfig, + config: Rc, } #[derive(Default)] pub struct RuleMatcher { rule_config: Vec>, - global_config: Option, + global_config: Option>, } impl RuleMatcher @@ -71,7 +72,7 @@ where let mut key_count = object.len(); if object.is_empty() { - self.global_config = Some(PluginConfig::default()); + self.global_config = Some(Rc::new(PluginConfig::default())); return Ok(()); } @@ -86,7 +87,7 @@ where if key_count > 0 { match serde_json::from_value::(config.clone()) { Ok(plugin_config) => { - self.global_config = Some(plugin_config); + self.global_config = Some(Rc::new(plugin_config)); } Err(err) => { log( @@ -134,14 +135,14 @@ where category, routes, hosts, - config, + config: Rc::new(config), }) } Ok(()) } - pub fn get_match_config(&self) -> Option<(i64, &PluginConfig)> { + pub fn get_match_config(&self) -> Option<(i64, Rc)> { let host = get_http_request_header(":authority").unwrap_or_default(); let route_name = get_property(vec!["route_name"]).unwrap_or_default(); @@ -149,7 +150,7 @@ where match rule.category { Category::Host => { if self.host_match(rule, host.as_str()) { - return Some((i as i64, &rule.config)); + return Some((i as i64, rule.config.clone())); } } Category::Route => { @@ -158,7 +159,7 @@ where .unwrap_or_else(|_| "".to_string()) .as_str(), ) { - return Some((i as i64, &rule.config)); + return Some((i as i64, rule.config.clone())); } } } @@ -166,14 +167,16 @@ where self.global_config .as_ref() - .map(|config| (usize::MAX as i64, config)) + .map(|config| (usize::MAX as i64, config.clone())) } pub fn rewrite_config(&mut self, rewrite: fn(config: &PluginConfig) -> PluginConfig) { - self.global_config = self.global_config.as_ref().map(rewrite); + if let Some(global_config) = &self.global_config { + self.global_config = Some(Rc::new(rewrite(global_config.borrow()))); + } for rule_config in &mut self.rule_config { - rule_config.config = rewrite(&rule_config.config); + rule_config.config = Rc::new(rewrite(rule_config.config.borrow())); } } From fc6902ded20a4bb0c3abf5e82ae271c59fd075a6 Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Mon, 21 Oct 2024 16:20:45 +0900 Subject: [PATCH 2/4] docs: add Japanese README and CONTRIBUTING files (#1407) --- CONTRIBUTING_JP.md | 195 +++++++++++++++++++++++++++++++++++++++++++++ README.md | 2 +- README_EN.md | 2 +- README_JP.md | 189 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 386 insertions(+), 2 deletions(-) create mode 100644 CONTRIBUTING_JP.md create mode 100644 README_JP.md diff --git a/CONTRIBUTING_JP.md b/CONTRIBUTING_JP.md new file mode 100644 index 0000000000..7916c56dd4 --- /dev/null +++ b/CONTRIBUTING_JP.md @@ -0,0 +1,195 @@ +# Higress への貢献 + +Higress のハッキングに興味がある場合は、温かく歓迎します。まず、このような意欲を非常に奨励します。そして、以下は貢献ガイドのリストです。 + +[[中文](./CONTRIBUTING.md)] | [[English Contributing Document](./CONTRIBUTING_EN.md)] + +## トピック + +- [Higress への貢献](#higress-への貢献) + - [トピック](#トピック) + - [セキュリティ問題の報告](#セキュリティ問題の報告) + - [一般的な問題の報告](#一般的な問題の報告) + - [コードとドキュメントの貢献](#コードとドキュメントの貢献) + - [ワークスペースの準備](#ワークスペースの準備) + - [ブランチの定義](#ブランチの定義) + - [コミットルール](#コミットルール) + - [コミットメッセージ](#コミットメッセージ) + - [コミット内容](#コミット内容) + - [PR 説明](#pr-説明) + - [テストケースの貢献](#テストケースの貢献) + - [何かを手伝うための参加](#何かを手伝うための参加) + - [コードスタイル](#コードスタイル) + +## セキュリティ問題の報告 + +セキュリティ問題は常に真剣に扱われます。通常の原則として、セキュリティ問題を広めることは推奨しません。Higress のセキュリティ問題を発見した場合は、公開で議論せず、公開の問題を開かないでください。代わりに、[higress@googlegroups.com](mailto:higress@googlegroups.com) にプライベートなメールを送信して報告することをお勧めします。 + +## 一般的な問題の報告 + +正直なところ、Higress のすべてのユーザーを非常に親切な貢献者と見なしています。Higress を体験した後、プロジェクトに対するフィードバックがあるかもしれません。その場合は、[NEW ISSUE](https://github.com/alibaba/higress/issues/new/choose) を通じて問題を開くことを自由に行ってください。 + +Higress プロジェクトを分散型で協力しているため、**よく書かれた**、**詳細な**、**明確な**問題報告を高く評価します。コミュニケーションをより効率的にするために、問題が検索リストに存在するかどうかを検索することを希望します。存在する場合は、新しい問題を開くのではなく、既存の問題のコメントに詳細を追加してください。 + +問題の詳細をできるだけ標準化するために、問題報告者のために [ISSUE TEMPLATE](./.github/ISSUE_TEMPLATE) を設定しました。テンプレートのフィールドに従って指示に従って記入してください。 + +問題を開く場合は多くのケースがあります: + +* バグ報告 +* 機能要求 +* パフォーマンス問題 +* 機能提案 +* 機能設計 +* 助けが必要 +* ドキュメントが不完全 +* テストの改善 +* プロジェクトに関する質問 +* その他 + +また、新しい問題を記入する際には、投稿から機密データを削除することを忘れないでください。機密データには、パスワード、秘密鍵、ネットワークの場所、プライベートなビジネスデータなどが含まれる可能性があります。 + +## コードとドキュメントの貢献 + +Higress プロジェクトをより良くするためのすべての行動が奨励されます。GitHub では、Higress のすべての改善は PR(プルリクエストの略)を通じて行うことができます。 + +* タイプミスを見つけた場合は、修正してみてください! +* バグを見つけた場合は、修正してみてください! +* 冗長なコードを見つけた場合は、削除してみてください! +* 欠落しているテストケースを見つけた場合は、追加してみてください! +* 機能を強化できる場合は、**ためらわないでください**! +* コードが不明瞭な場合は、コメントを追加して明確にしてください! +* コードが醜い場合は、リファクタリングしてみてください! +* ドキュメントの改善に役立つ場合は、さらに良いです! +* ドキュメントが不正確な場合は、修正してください! +* ... + +実際には、それらを完全にリストすることは不可能です。1つの原則を覚えておいてください: + +> あなたからの PR を楽しみにしています。 + +Higress を PR で改善する準備ができたら、ここで PR ルールを確認することをお勧めします。 + +* [ワークスペースの準備](#ワークスペースの準備) +* [ブランチの定義](#ブランチの定義) +* [コミットルール](#コミットルール) +* [PR 説明](#pr-説明) + +### ワークスペースの準備 + +PR を提出するために、GitHub ID に登録していることを前提とします。その後、以下の手順で準備を完了できます: + +1. Higress を自分のリポジトリに **FORK** します。この作業を行うには、[alibaba/higress](https://github.com/alibaba/higress) のメインページの右上にある Fork ボタンをクリックするだけです。その後、`https://github.com//higress` に自分のリポジトリが作成されます。ここで、`your-username` はあなたの GitHub ユーザー名です。 + +2. 自分のリポジトリをローカルに **CLONE** します。`git clone git@github.com:/higress.git` を使用してリポジトリをローカルマシンにクローンします。その後、新しいブランチを作成して、行いたい変更を完了できます。 + +3. リモートを `git@github.com:alibaba/higress.git` に設定します。以下の2つのコマンドを使用します: + +```bash +git remote add upstream git@github.com:alibaba/higress.git +git remote set-url --push upstream no-pushing +``` + +このリモート設定を使用すると、git リモート設定を次のように確認できます: + +```shell +$ git remote -v +origin git@github.com:/higress.git (fetch) +origin git@github.com:/higress.git (push) +upstream git@github.com:alibaba/higress.git (fetch) +upstream no-pushing (push) +``` + +これを追加すると、ローカルブランチを上流ブランチと簡単に同期できます。 + +### ブランチの定義 + +現在、プルリクエストを通じたすべての貢献は Higress の [main ブランチ](https://github.com/alibaba/higress/tree/main) に対するものであると仮定します。貢献する前に、ブランチの定義を理解することは非常に役立ちます。 + +貢献者として、プルリクエストを通じたすべての貢献は main ブランチに対するものであることを再度覚えておいてください。Higress プロジェクトには、リリースブランチ(例:0.6.0、0.6.1)、機能ブランチ、ホットフィックスブランチなど、いくつかの他のブランチがあります。 + +正式にバージョンをリリースする際には、リリースブランチが作成され、バージョン番号で命名されます。 + +リリース後、リリースブランチのコミットを main ブランチにマージします。 + +特定のバージョンにバグがある場合、後のバージョンで修正するか、特定のホットフィックスバージョンで修正するかを決定します。ホットフィックスバージョンで修正することを決定した場合、対応するリリースブランチに基づいてホットフィックスブランチをチェックアウトし、コード修正と検証を行い、main ブランチにマージします。 + +大きな機能については、開発と検証のために機能ブランチを引き出します。 + +### コミットルール + +実際には、Higress ではコミット時に2つのルールを真剣に考えています: + +* [コミットメッセージ](#コミットメッセージ) +* [コミット内容](#コミット内容) + +#### コミットメッセージ + +コミットメッセージは、提出された PR の目的をレビュアーがよりよく理解するのに役立ちます。また、コードレビューの手続きを加速するのにも役立ちます。貢献者には、曖昧なメッセージではなく、**明確な**コミットメッセージを使用することを奨励します。一般的に、以下のコミットメッセージタイプを推奨します: + +* docs: xxxx. 例:"docs: add docs about Higress cluster installation". +* feature: xxxx. 例:"feature: use higress config instead of istio config". +* bugfix: xxxx. 例:"bugfix: fix panic when input nil parameter". +* refactor: xxxx. 例:"refactor: simplify to make codes more readable". +* test: xxx. 例:"test: add unit test case for func InsertIntoArray". +* その他の読みやすく明確な表現方法。 + +一方で、以下のような方法でのコミットメッセージは推奨しません: + +* ~~バグ修正~~ +* ~~更新~~ +* ~~ドキュメント追加~~ + +迷った場合は、[Git コミットメッセージの書き方](http://chris.beams.io/posts/git-commit/) を参照してください。 + +#### コミット内容 + +コミット内容は、1つのコミットに含まれるすべての内容の変更を表します。1つのコミットに、他のコミットの助けを借りずにレビュアーが完全にレビューできる内容を含めるのが最善です。言い換えれば、1つのコミットの内容は CI を通過でき、コードの混乱を避けることができます。簡単に言えば、次の3つの小さなルールを覚えておく必要があります: + +* コミットで非常に大きな変更を避ける; +* 各コミットが完全でレビュー可能であること。 +* コミット時に git config(`user.name`、`user.email`)を確認して、それが GitHub ID に関連付けられていることを確認します。 + +```bash +git config --get user.name +git config --get user.email +``` + +* pr を提出する際には、'changes/' フォルダーの下の XXX.md ファイルに現在の変更の簡単な説明を追加してください。 + +さらに、コード変更部分では、すべての貢献者が Higress の [コードスタイル](#コードスタイル) を読むことをお勧めします。 + +コミットメッセージやコミット内容に関係なく、コードレビューに重点を置いています。 + +### PR 説明 + +PR は Higress プロジェクトファイルを変更する唯一の方法です。レビュアーが目的をよりよく理解できるようにするために、PR 説明は詳細すぎることはありません。貢献者には、[PR テンプレート](./.github/PULL_REQUEST_TEMPLATE.md) に従ってプルリクエストを完了することを奨励します。 + +### 開発前の準備 + +```shell +make prebuild && go mod tidy +``` + +## テストケースの貢献 + +テストケースは歓迎されます。現在、Higress の機能テストケースが高優先度です。 + +* 単体テストの場合、同じモジュールの test ディレクトリに xxxTest.go という名前のテストファイルを作成する必要があります。 +* 統合テストの場合、統合テストを test ディレクトリに配置できます。 +//TBD + +## 何かを手伝うための参加 + +GitHub を Higress の協力の主要な場所として選択しました。したがって、Higress の最新の更新は常にここにあります。PR を通じた貢献は明確な助けの方法ですが、他の方法も呼びかけています。 + +* 可能であれば、他の人の質問に返信する; +* 他のユーザーの問題を解決するのを手伝う; +* 他の人の PR 設計をレビューするのを手伝う; +* 他の人の PR のコードをレビューするのを手伝う; +* Higress について議論して、物事を明確にする; +* GitHub 以外で Higress 技術を宣伝する; +* Higress に関するブログを書くなど。 + +## コードスタイル +//TBD +要するに、**どんな助けも貢献です。** diff --git a/README.md b/README.md index 87468ed9ad..761f8591a8 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@

- English | 中文 + English | 中文 | 日本語

diff --git a/README_EN.md b/README_EN.md index 8ce33aa069..b099fa3e5b 100644 --- a/README_EN.md +++ b/README_EN.md @@ -15,7 +15,7 @@

- English | 中文 + English | 中文 | 日本語

Higress is a cloud-native api gateway based on Alibaba's internal gateway practices. diff --git a/README_JP.md b/README_JP.md new file mode 100644 index 0000000000..8573576ee0 --- /dev/null +++ b/README_JP.md @@ -0,0 +1,189 @@ +

+ Higress +
+ AIゲートウェイ +

+

AIネイティブAPIゲートウェイ

+ +[![Build Status](https://github.com/alibaba/higress/actions/workflows/build-and-test.yaml/badge.svg?branch=main)](https://github.com/alibaba/higress/actions) +[![license](https://img.shields.io/github/license/alibaba/higress.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) + +[**公式サイト**](https://higress.cn/)   | +  [**ドキュメント**](https://higress.cn/docs/latest/overview/what-is-higress/)   | +  [**ブログ**](https://higress.cn/blog/)   | +  [**電子書籍**](https://higress.cn/docs/ebook/wasm14/)   | +  [**開発ガイド**](https://higress.cn/docs/latest/dev/architecture/)   | +  [**AIプラグイン**](https://higress.cn/plugin/)   + + +

+ English | 中文 | 日本語 +

+ + +Higressは、IstioとEnvoyをベースにしたクラウドネイティブAPIゲートウェイで、Go/Rust/JSなどを使用してWasmプラグインを作成できます。数十の既製の汎用プラグインと、すぐに使用できるコンソールを提供しています(デモは[こちら](http://demo.higress.io/))。 + +Higressは、Tengineのリロードが長時間接続のビジネスに影響を与える問題や、gRPC/Dubboの負荷分散能力の不足を解決するために、Alibaba内部で誕生しました。 + +Alibaba Cloudは、Higressを基盤にクラウドネイティブAPIゲートウェイ製品を構築し、多くの企業顧客に99.99%のゲートウェイ高可用性保証サービスを提供しています。 + +Higressは、AIゲートウェイ機能を基盤に、Tongyi Qianwen APP、Bailian大規模モデルAPI、機械学習PAIプラットフォームなどのAIビジネスをサポートしています。また、国内の主要なAIGC企業(例:ZeroOne)やAI製品(例:FastGPT)にもサービスを提供しています。 + +![](https://img.alicdn.com/imgextra/i2/O1CN011AbR8023V8R5N0HcA_!!6000000007260-2-tps-1080-606.png) + + +## 目次 + +- [**クイックスタート**](#クイックスタート) +- [**機能紹介**](#機能紹介) +- [**使用シナリオ**](#使用シナリオ) +- [**主な利点**](#主な利点) +- [**コミュニティ**](#コミュニティ) + +## クイックスタート + +HigressはDockerだけで起動でき、個人開発者がローカルで学習用にセットアップしたり、簡易サイトを構築するのに便利です。 + +```bash +# 作業ディレクトリを作成 +mkdir higress; cd higress +# Higressを起動し、設定ファイルを作業ディレクトリに書き込みます +docker run -d --rm --name higress-ai -v ${PWD}:/data \ + -p 8001:8001 -p 8080:8080 -p 8443:8443 \ + higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/all-in-one:latest +``` + +リスンポートの説明は以下の通りです: + +- 8001ポート:Higress UIコンソールのエントリーポイント +- 8080ポート:ゲートウェイのHTTPプロトコルエントリーポイント +- 8443ポート:ゲートウェイのHTTPSプロトコルエントリーポイント + +**HigressのすべてのDockerイメージは専用のリポジトリを使用しており、Docker Hubの国内アクセス不可の影響を受けません** + +K8sでのHelmデプロイなどの他のインストール方法については、公式サイトの[クイックスタートドキュメント](https://higress.cn/docs/latest/user/quickstart/)を参照してください。 + + +## 使用シナリオ + +- **AIゲートウェイ**: + + Higressは、国内外のすべてのLLMモデルプロバイダーと統一されたプロトコルで接続でき、豊富なAI可観測性、多モデル負荷分散/フォールバック、AIトークンフロー制御、AIキャッシュなどの機能を備えています。 + + ![](https://img.alicdn.com/imgextra/i1/O1CN01fNnhCp1cV8mYPRFeS_!!6000000003605-0-tps-1080-608.jpg) + +- **Kubernetes Ingressゲートウェイ**: + + HigressはK8sクラスターのIngressエントリーポイントゲートウェイとして機能し、多くのK8s Nginx Ingressの注釈に対応しています。K8s Nginx IngressからHigressへのスムーズな移行が可能です。 + + [Gateway API](https://gateway-api.sigs.k8s.io/)標準をサポートし、ユーザーがIngress APIからGateway APIにスムーズに移行できるようにします。 + + ingress-nginxと比較して、リソースの消費が大幅に減少し、ルーティングの変更が10倍速く反映されます。 + + ![](https://img.alicdn.com/imgextra/i1/O1CN01bhEtb229eeMNBWmdP_!!6000000008093-2-tps-750-547.png) + ![](https://img.alicdn.com/imgextra/i1/O1CN01bqRets1LsBGyitj4S_!!6000000001354-2-tps-887-489.png) + +- **マイクロサービスゲートウェイ**: + + Higressはマイクロサービスゲートウェイとして機能し、Nacos、ZooKeeper、Consul、Eurekaなどのさまざまなサービスレジストリからサービスを発見し、ルーティングを構成できます。 + + また、[Dubbo](https://github.com/apache/dubbo)、[Nacos](https://github.com/alibaba/nacos)、[Sentinel](https://github.com/alibaba/Sentinel)などのマイクロサービス技術スタックと深く統合されています。Envoy C++ゲートウェイコアの優れたパフォーマンスに基づいて、従来のJavaベースのマイクロサービスゲートウェイと比較して、リソース使用率を大幅に削減し、コストを削減できます。 + + ![](https://img.alicdn.com/imgextra/i4/O1CN01v4ZbCj1dBjePSMZ17_!!6000000003698-0-tps-1613-926.jpg) + +- **セキュリティゲートウェイ**: + + Higressはセキュリティゲートウェイとして機能し、WAF機能を提供し、key-auth、hmac-auth、jwt-auth、basic-auth、oidcなどのさまざまな認証戦略をサポートします。 + +## 主な利点 + +- **プロダクションレベル** + + Alibabaで2年以上のプロダクション検証を経た内部製品から派生し、毎秒数十万のリクエストを処理する大規模なシナリオをサポートします。 + + Nginxのリロードによるトラフィックの揺れを完全に排除し、構成変更がミリ秒単位で反映され、ビジネスに影響を与えません。AIビジネスなどの長時間接続シナリオに特に適しています。 + +- **ストリーム処理** + + リクエスト/レスポンスボディの完全なストリーム処理をサポートし、Wasmプラグインを使用してSSE(Server-Sent Events)などのストリームプロトコルのメッセージをカスタマイズして処理できます。 + + AIビジネスなどの大帯域幅シナリオで、メモリ使用量を大幅に削減できます。 + +- **拡張性** + + AI、トラフィック管理、セキュリティ保護などの一般的な機能をカバーする豊富な公式プラグインライブラリを提供し、90%以上のビジネスシナリオのニーズを満たします。 + + Wasmプラグイン拡張を主力とし、サンドボックス隔離を通じてメモリの安全性を確保し、複数のプログラミング言語をサポートし、プラグインバージョンの独立したアップグレードを許可し、トラフィックに影響を与えずにゲートウェイロジックをホットアップデートできます。 + +- **安全で使いやすい** + + Ingress APIおよびGateway API標準に基づき、すぐに使用できるUIコンソールを提供し、WAF保護プラグイン、IP/Cookie CC保護プラグインをすぐに使用できます。 + + Let's Encryptの自動証明書発行および更新をサポートし、K8sを使用せずにデプロイでき、1行のDockerコマンドで起動でき、個人開発者にとって便利です。 + + +## 機能紹介 + +### AIゲートウェイデモ展示 + +[OpenAIから他の大規模モデルへの移行を30秒で完了 +](https://www.bilibili.com/video/BV1dT421a7w7/?spm_id_from=333.788.recommend_more_video.14) + + +### Higress UIコンソール + +- **豊富な可観測性** + + すぐに使用できる可観測性を提供し、Grafana&Prometheusは組み込みのものを使用することも、自分で構築したものを接続することもできます。 + + ![](./docs/images/monitor.gif) + + +- **プラグイン拡張メカニズム** + + 公式にはさまざまなプラグインが提供されており、ユーザーは[独自のプラグインを開発](./plugins/wasm-go)し、Docker/OCIイメージとして構築し、コンソールで構成して、プラグインロジックをリアルタイムで変更できます。トラフィックに影響を与えずにプラグインロジックをホットアップデートできます。 + + ![](./docs/images/plugin.gif) + + +- **さまざまなサービス発見** + + デフォルトでK8s Serviceサービス発見を提供し、構成を通じてNacos/ZooKeeperなどのレジストリに接続してサービスを発見することも、静的IPまたはDNSに基づいて発見することもできます。 + + ![](./docs/images/service-source.gif) + + +- **ドメインと証明書** + + TLS証明書を作成および管理し、ドメインのHTTP/HTTPS動作を構成できます。ドメインポリシーでは、特定のドメインに対してプラグインを適用することができます。 + + ![](./docs/images/domain.gif) + + +- **豊富なルーティング機能** + + 上記で定義されたサービス発見メカニズムを通じて、発見されたサービスはサービスリストに表示されます。ルーティングを作成する際に、ドメインを選択し、ルーティングマッチングメカニズムを定義し、ターゲットサービスを選択してルーティングを行います。ルーティングポリシーでは、特定のルーティングに対してプラグインを適用することができます。 + + ![](./docs/images/route-service.gif) + + +## コミュニティ + +### 感謝 + +EnvoyとIstioのオープンソースの取り組みがなければ、Higressは実現できませんでした。これらのプロジェクトに最も誠実な敬意を表します。 + +### 交流グループ + +![image](https://img.alicdn.com/imgextra/i2/O1CN01BkopaB22ZsvamFftE_!!6000000007135-0-tps-720-405.jpg) + +### 技術共有 + +WeChat公式アカウント: + +![](https://img.alicdn.com/imgextra/i1/O1CN01WnQt0q1tcmqVDU73u_!!6000000005923-0-tps-258-258.jpg) + +### 関連リポジトリ + +- Higressコンソール:https://github.com/higress-group/higress-console +- Higress(スタンドアロン版):https://github.com/higress-group/higress-standalone From badf4b7101526f2a8cb9dd7cc76d6cc06cb55e24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BE=84=E6=BD=AD?= Date: Mon, 21 Oct 2024 15:43:01 +0800 Subject: [PATCH 3/4] ai cache plugin support set skip ai cache header (#1380) --- plugins/wasm-go/extensions/ai-cache/README.md | 5 +++++ plugins/wasm-go/extensions/ai-cache/README_EN.md | 4 ++++ plugins/wasm-go/extensions/ai-cache/main.go | 12 ++++++++++++ 3 files changed, 21 insertions(+) diff --git a/plugins/wasm-go/extensions/ai-cache/README.md b/plugins/wasm-go/extensions/ai-cache/README.md index 97728f5177..1de252f12c 100644 --- a/plugins/wasm-go/extensions/ai-cache/README.md +++ b/plugins/wasm-go/extensions/ai-cache/README.md @@ -9,6 +9,11 @@ description: AI 缓存插件配置参考 LLM 结果缓存插件,默认配置方式可以直接用于 openai 协议的结果缓存,同时支持流式和非流式响应的缓存。 +**提示** + +携带请求头`x-higress-skip-ai-cache: on`时,当前请求将不会使用缓存中的内容,而是直接转发给后端服务,同时也不会缓存该请求返回响应的内容 + + ## 运行属性 插件执行阶段:`认证阶段` diff --git a/plugins/wasm-go/extensions/ai-cache/README_EN.md b/plugins/wasm-go/extensions/ai-cache/README_EN.md index 81099e509c..7544995999 100644 --- a/plugins/wasm-go/extensions/ai-cache/README_EN.md +++ b/plugins/wasm-go/extensions/ai-cache/README_EN.md @@ -6,6 +6,10 @@ description: AI Cache Plugin Configuration Reference ## Function Description LLM result caching plugin, the default configuration can be directly used for result caching under the OpenAI protocol, and it supports caching of both streaming and non-streaming responses. +**Tips** + +When carrying the request header `x-higress-skip-ai-cache: on`, the current request will not use content from the cache but will be directly forwarded to the backend service. Additionally, the response content from this request will not be cached. + ## Runtime Properties Plugin Execution Phase: `Authentication Phase` Plugin Execution Priority: `10` diff --git a/plugins/wasm-go/extensions/ai-cache/main.go b/plugins/wasm-go/extensions/ai-cache/main.go index dc5df1a6a8..7886d5698f 100644 --- a/plugins/wasm-go/extensions/ai-cache/main.go +++ b/plugins/wasm-go/extensions/ai-cache/main.go @@ -22,6 +22,7 @@ const ( ToolCallsContextKey = "toolCalls" StreamContextKey = "stream" DefaultCacheKeyPrefix = "higress-ai-cache:" + SkipCacheHeader = "x-higress-skip-ai-cache" ) func main() { @@ -172,6 +173,12 @@ func parseConfig(json gjson.Result, c *PluginConfig, log wrapper.Log) error { } func onHttpRequestHeaders(ctx wrapper.HttpContext, config PluginConfig, log wrapper.Log) types.Action { + skipCache, _ := proxywasm.GetHttpRequestHeader(SkipCacheHeader) + if skipCache == "on" { + ctx.SetContext(SkipCacheHeader, struct{}{}) + ctx.DontReadRequestBody() + return types.ActionContinue + } contentType, _ := proxywasm.GetHttpRequestHeader("content-type") // The request does not have a body. if contentType == "" { @@ -270,6 +277,11 @@ func processSSEMessage(ctx wrapper.HttpContext, config PluginConfig, sseMessage } func onHttpResponseHeaders(ctx wrapper.HttpContext, config PluginConfig, log wrapper.Log) types.Action { + skipCache := ctx.GetContext(SkipCacheHeader) + if skipCache != nil { + ctx.DontReadResponseBody() + return types.ActionContinue + } contentType, _ := proxywasm.GetHttpResponseHeader("content-type") if strings.Contains(contentType, "text/event-stream") { ctx.SetContext(StreamContextKey, struct{}{}) From f8d62a8ac3d8ff56b9784f4e41a120ff514ca979 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BE=84=E6=BD=AD?= Date: Mon, 21 Oct 2024 16:46:18 +0800 Subject: [PATCH 4/4] add model router plugin (#1414) --- plugins/wasm-cpp/WORKSPACE | 6 +- plugins/wasm-cpp/bazel/wasm.bzl | 6 +- .../wasm-cpp/extensions/model_router/BUILD | 70 +++++++ .../extensions/model_router/README.md | 64 ++++++ .../extensions/model_router/README_EN.md | 63 ++++++ .../extensions/model_router/plugin.cc | 189 ++++++++++++++++++ .../wasm-cpp/extensions/model_router/plugin.h | 85 ++++++++ .../extensions/model_router/plugin_test.cc | 144 +++++++++++++ 8 files changed, 621 insertions(+), 6 deletions(-) create mode 100644 plugins/wasm-cpp/extensions/model_router/BUILD create mode 100644 plugins/wasm-cpp/extensions/model_router/README.md create mode 100644 plugins/wasm-cpp/extensions/model_router/README_EN.md create mode 100644 plugins/wasm-cpp/extensions/model_router/plugin.cc create mode 100644 plugins/wasm-cpp/extensions/model_router/plugin.h create mode 100644 plugins/wasm-cpp/extensions/model_router/plugin_test.cc diff --git a/plugins/wasm-cpp/WORKSPACE b/plugins/wasm-cpp/WORKSPACE index dc55483d9c..ed78d0df06 100644 --- a/plugins/wasm-cpp/WORKSPACE +++ b/plugins/wasm-cpp/WORKSPACE @@ -16,15 +16,15 @@ load("@io_bazel_rules_docker//repositories:deps.bzl", container_deps = "deps") container_deps() -PROXY_WASM_CPP_SDK_SHA = "fd0be8405db25de0264bdb78fae3a82668c03782" +PROXY_WASM_CPP_SDK_SHA = "eaec483b5b3c7bcb89fd208b5a1fa5d79d626f61" -PROXY_WASM_CPP_SDK_SHA256 = "c57de2425b5c61d7f630c5061e319b4557ae1f1c7526e5a51c33dc1299471b08" +PROXY_WASM_CPP_SDK_SHA256 = "1140bc8114d75db56a6ca6b18423d4df50d988d40b4cec929a1eb246cf5a4a3d" http_archive( name = "proxy_wasm_cpp_sdk", sha256 = PROXY_WASM_CPP_SDK_SHA256, strip_prefix = "proxy-wasm-cpp-sdk-" + PROXY_WASM_CPP_SDK_SHA, - url = "https://github.com/proxy-wasm/proxy-wasm-cpp-sdk/archive/" + PROXY_WASM_CPP_SDK_SHA + ".tar.gz", + url = "https://github.com/higress-group/proxy-wasm-cpp-sdk/archive/" + PROXY_WASM_CPP_SDK_SHA + ".tar.gz", ) load("@proxy_wasm_cpp_sdk//bazel/dep:deps.bzl", "wasm_dependencies") diff --git a/plugins/wasm-cpp/bazel/wasm.bzl b/plugins/wasm-cpp/bazel/wasm.bzl index a100ccc0b6..1f061fbc69 100644 --- a/plugins/wasm-cpp/bazel/wasm.bzl +++ b/plugins/wasm-cpp/bazel/wasm.bzl @@ -33,14 +33,14 @@ def wasm_libraries(): urls = ["https://github.com/google/googletest/archive/release-1.10.0.tar.gz"], ) - PROXY_WASM_CPP_HOST_SHA = "f38347360feaaf5b2a733f219c4d8c9660d626f0" - PROXY_WASM_CPP_HOST_SHA256 = "bf10de946eb5785813895c2bf16504afc0cd590b9655d9ee52fb1074d0825ea3" + PROXY_WASM_CPP_HOST_SHA = "7850d1721fe3dd2ccfb86a06116f76c23b1f1bf8" + PROXY_WASM_CPP_HOST_SHA256 = "740690fc1d749849f6e24b5bc48a07dabc0565a7d03b6cd13425dba693956c57" http_archive( name = "proxy_wasm_cpp_host", sha256 = PROXY_WASM_CPP_HOST_SHA256, strip_prefix = "proxy-wasm-cpp-host-" + PROXY_WASM_CPP_HOST_SHA, - url = "https://github.com/proxy-wasm/proxy-wasm-cpp-host/archive/" + PROXY_WASM_CPP_HOST_SHA +".tar.gz", + url = "https://github.com/higress-group/proxy-wasm-cpp-host/archive/" + PROXY_WASM_CPP_HOST_SHA +".tar.gz", ) http_archive( diff --git a/plugins/wasm-cpp/extensions/model_router/BUILD b/plugins/wasm-cpp/extensions/model_router/BUILD new file mode 100644 index 0000000000..67cfa547db --- /dev/null +++ b/plugins/wasm-cpp/extensions/model_router/BUILD @@ -0,0 +1,70 @@ +# Copyright (c) 2022 Alibaba Group Holding Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("@proxy_wasm_cpp_sdk//bazel/wasm:wasm.bzl", "wasm_cc_binary") +load("//bazel:wasm.bzl", "declare_wasm_image_targets") + +wasm_cc_binary( + name = "model_router.wasm", + srcs = [ + "plugin.cc", + "plugin.h", + ], + deps = [ + "@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics_higress", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/time", + "//common:json_util", + "//common:http_util", + "//common:rule_util", + ], +) + +cc_library( + name = "model_router_lib", + srcs = [ + "plugin.cc", + ], + hdrs = [ + "plugin.h", + ], + copts = ["-DNULL_PLUGIN"], + deps = [ + "@com_google_absl//absl/strings", + "@com_google_absl//absl/time", + "//common:json_util", + "@proxy_wasm_cpp_host//:lib", + "//common:http_util_nullvm", + "//common:rule_util_nullvm", + ], +) + +cc_test( + name = "model_router_test", + srcs = [ + "plugin_test.cc", + ], + copts = ["-DNULL_PLUGIN"], + deps = [ + ":model_router_lib", + "@com_google_googletest//:gtest", + "@com_google_googletest//:gtest_main", + "@proxy_wasm_cpp_host//:lib", + ], +) + +declare_wasm_image_targets( + name = "model_router", + wasm_file = ":model_router.wasm", +) diff --git a/plugins/wasm-cpp/extensions/model_router/README.md b/plugins/wasm-cpp/extensions/model_router/README.md new file mode 100644 index 0000000000..b63be35d8f --- /dev/null +++ b/plugins/wasm-cpp/extensions/model_router/README.md @@ -0,0 +1,64 @@ +## 功能说明 +`model-router`插件实现了基于LLM协议中的model参数路由的功能 + +## 运行属性 + +插件执行阶段:`默认阶段` +插件执行优先级:`260` + +## 配置字段 + +| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 | +| ----------- | --------------- | ----------------------- | ------ | ------------------------------------------- | +| `enable` | bool | 选填 | false | 是否开启基于model参数路由 | +| `model_key` | string | 选填 | model | 请求body中model参数的位置 | +| `add_header_key` | string | 选填 | x-higress-llm-provider | 从model参数中解析出的provider名字放到哪个请求header中 | + + +## 效果说明 + +如下开启基于model参数路由的功能: + +```yaml +enable: true +``` + +开启后,插件将请求中 model 参数的 provider 部分(如果有)提取出来,设置到 x-higress-llm-provider 这个请求 header 中,用于后续路由,并将 model 参数重写为模型名称部分。举例来说,原生的 LLM 请求体是: + +```json +{ + "model": "qwen/qwen-long", + "frequency_penalty": 0, + "max_tokens": 800, + "stream": false, + "messages": [{ + "role": "user", + "content": "higress项目主仓库的github地址是什么" + }], + "presence_penalty": 0, + "temperature": 0.7, + "top_p": 0.95 +} +``` + +经过这个插件后,将添加下面这个请求头(可以用于路由匹配): + +x-higress-llm-provider: qwen + +原始的 LLM 请求体将被改成: + +```json +{ + "model": "qwen-long", + "frequency_penalty": 0, + "max_tokens": 800, + "stream": false, + "messages": [{ + "role": "user", + "content": "higress项目主仓库的github地址是什么" + }], + "presence_penalty": 0, + "temperature": 0.7, + "top_p": 0.95 +} +``` diff --git a/plugins/wasm-cpp/extensions/model_router/README_EN.md b/plugins/wasm-cpp/extensions/model_router/README_EN.md new file mode 100644 index 0000000000..4d2eaf1fee --- /dev/null +++ b/plugins/wasm-cpp/extensions/model_router/README_EN.md @@ -0,0 +1,63 @@ +## Function Description +The `model-router` plugin implements the functionality of routing based on the `model` parameter in the LLM protocol. + +## Runtime Properties + +Plugin Execution Phase: `Default Phase` +Plugin Execution Priority: `260` + +## Configuration Fields + +| Name | Data Type | Filling Requirement | Default Value | Description | +| -------------------- | ------------- | --------------------- | ---------------------- | ----------------------------------------------------- | +| `enable` | bool | Optional | false | Whether to enable routing based on the `model` parameter | +| `model_key` | string | Optional | model | The location of the `model` parameter in the request body | +| `add_header_key` | string | Optional | x-higress-llm-provider | The header where the parsed provider name from the `model` parameter will be placed | + +## Effect Description + +To enable routing based on the `model` parameter, use the following configuration: + +```yaml +enable: true +``` + +After enabling, the plugin extracts the provider part (if any) from the `model` parameter in the request, and sets it in the `x-higress-llm-provider` request header for subsequent routing. It also rewrites the `model` parameter to the model name part. For example, the original LLM request body is: + +```json +{ + "model": "openai/gpt-4o", + "frequency_penalty": 0, + "max_tokens": 800, + "stream": false, + "messages": [{ + "role": "user", + "content": "What is the GitHub address for the main repository of the Higress project?" + }], + "presence_penalty": 0, + "temperature": 0.7, + "top_p": 0.95 +} +``` + +After processing by the plugin, the following request header (which can be used for routing matching) will be added: + +`x-higress-llm-provider: openai` + +The original LLM request body will be modified to: + +```json +{ + "model": "gpt-4o", + "frequency_penalty": 0, + "max_tokens": 800, + "stream": false, + "messages": [{ + "role": "user", + "content": "What is the GitHub address for the main repository of the Higress project?" + }], + "presence_penalty": 0, + "temperature": 0.7, + "top_p": 0.95 +} +``` diff --git a/plugins/wasm-cpp/extensions/model_router/plugin.cc b/plugins/wasm-cpp/extensions/model_router/plugin.cc new file mode 100644 index 0000000000..457864d268 --- /dev/null +++ b/plugins/wasm-cpp/extensions/model_router/plugin.cc @@ -0,0 +1,189 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "extensions/model_router/plugin.h" + +#include +#include + +#include "absl/strings/str_cat.h" +#include "absl/strings/str_split.h" +#include "common/http_util.h" +#include "common/json_util.h" + +using ::nlohmann::json; +using ::Wasm::Common::JsonArrayIterate; +using ::Wasm::Common::JsonGetField; +using ::Wasm::Common::JsonObjectIterate; +using ::Wasm::Common::JsonValueAs; + +#ifdef NULL_PLUGIN + +namespace proxy_wasm { +namespace null_plugin { +namespace model_router { + +PROXY_WASM_NULL_PLUGIN_REGISTRY + +#endif + +static RegisterContextFactory register_ModelRouter( + CONTEXT_FACTORY(PluginContext), ROOT_FACTORY(PluginRootContext)); + +namespace { + +constexpr std::string_view SetDecoderBufferLimitKey = + "SetRequestBodyBufferLimit"; +constexpr std::string_view DefaultMaxBodyBytes = "10485760"; + +} // namespace + +bool PluginRootContext::parsePluginConfig(const json& configuration, + ModelRouterConfigRule& rule) { + if (auto it = configuration.find("enable"); it != configuration.end()) { + if (it->is_boolean()) { + rule.enable_ = it->get(); + } else { + LOG_WARN("Invalid type for enable. Expected boolean."); + return false; + } + } + + if (auto it = configuration.find("model_key"); it != configuration.end()) { + if (it->is_string()) { + rule.model_key_ = it->get(); + } else { + LOG_WARN("Invalid type for model_key. Expected string."); + return false; + } + } + + if (auto it = configuration.find("add_header_key"); + it != configuration.end()) { + if (it->is_string()) { + rule.add_header_key_ = it->get(); + } else { + LOG_WARN("Invalid type for add_header_key. Expected string."); + return false; + } + } + + return true; +} + +bool PluginRootContext::onConfigure(size_t size) { + // Parse configuration JSON string. + if (size > 0 && !configure(size)) { + LOG_WARN("configuration has errors initialization will not continue."); + return false; + } + return true; +} + +bool PluginRootContext::configure(size_t configuration_size) { + auto configuration_data = getBufferBytes(WasmBufferType::PluginConfiguration, + 0, configuration_size); + // Parse configuration JSON string. + auto result = ::Wasm::Common::JsonParse(configuration_data->view()); + if (!result) { + LOG_WARN(absl::StrCat("cannot parse plugin configuration JSON string: ", + configuration_data->view())); + return false; + } + if (!parseAuthRuleConfig(result.value())) { + LOG_WARN(absl::StrCat("cannot parse plugin configuration JSON string: ", + configuration_data->view())); + return false; + } + return true; +} + +FilterHeadersStatus PluginRootContext::onHeader( + const ModelRouterConfigRule& rule) { + if (!rule.enable_ || !Wasm::Common::Http::hasRequestBody()) { + return FilterHeadersStatus::Continue; + } + auto content_type_value = + getRequestHeader(Wasm::Common::Http::Header::ContentType); + if (!absl::StrContains(content_type_value->view(), + Wasm::Common::Http::ContentTypeValues::Json)) { + return FilterHeadersStatus::Continue; + } + removeRequestHeader(Wasm::Common::Http::Header::ContentLength); + setFilterState(SetDecoderBufferLimitKey, DefaultMaxBodyBytes); + return FilterHeadersStatus::StopIteration; +} + +FilterDataStatus PluginRootContext::onBody(const ModelRouterConfigRule& rule, + std::string_view body) { + const auto& model_key = rule.model_key_; + const auto& add_header_key = rule.add_header_key_; + auto body_json_opt = ::Wasm::Common::JsonParse(body); + if (!body_json_opt) { + LOG_WARN(absl::StrCat("cannot parse body to JSON string: ", body)); + return FilterDataStatus::Continue; + } + auto body_json = body_json_opt.value(); + if (body_json.contains(model_key)) { + std::string model_value = body_json[model_key]; + auto pos = model_value.find('/'); + if (pos != std::string::npos) { + const auto& provider = model_value.substr(0, pos); + const auto& model = model_value.substr(pos + 1); + replaceRequestHeader(add_header_key, provider); + body_json[model_key] = model; + setBuffer(WasmBufferType::HttpRequestBody, 0, + std::numeric_limits::max(), body_json.dump()); + LOG_DEBUG(absl::StrCat("model route to provider:", provider, + ", model:", model)); + } else { + LOG_DEBUG(absl::StrCat("model route not work, model:", model_value)); + } + } + return FilterDataStatus::Continue; +} + +FilterHeadersStatus PluginContext::onRequestHeaders(uint32_t, bool) { + auto* rootCtx = rootContext(); + return rootCtx->onHeaders([rootCtx, this](const auto& config) { + auto ret = rootCtx->onHeader(config); + if (ret == FilterHeadersStatus::StopIteration) { + this->config_ = &config; + } + return ret; + }); +} + +FilterDataStatus PluginContext::onRequestBody(size_t body_size, + bool end_stream) { + if (config_ == nullptr) { + return FilterDataStatus::Continue; + } + body_total_size_ += body_size; + if (!end_stream) { + return FilterDataStatus::StopIterationAndBuffer; + } + auto body = + getBufferBytes(WasmBufferType::HttpRequestBody, 0, body_total_size_); + auto* rootCtx = rootContext(); + return rootCtx->onBody(*config_, body->view()); +} + +#ifdef NULL_PLUGIN + +} // namespace model_router +} // namespace null_plugin +} // namespace proxy_wasm + +#endif diff --git a/plugins/wasm-cpp/extensions/model_router/plugin.h b/plugins/wasm-cpp/extensions/model_router/plugin.h new file mode 100644 index 0000000000..16cfdf8509 --- /dev/null +++ b/plugins/wasm-cpp/extensions/model_router/plugin.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2022 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include "common/route_rule_matcher.h" +#define ASSERT(_X) assert(_X) + +#ifndef NULL_PLUGIN + +#include "proxy_wasm_intrinsics.h" + +#else + +#include "include/proxy-wasm/null_plugin.h" + +namespace proxy_wasm { +namespace null_plugin { +namespace model_router { + +#endif + +struct ModelRouterConfigRule { + bool enable_ = false; + std::string model_key_ = "model"; + std::string add_header_key_ = "x-higress-llm-provider"; +}; + +// PluginRootContext is the root context for all streams processed by the +// thread. It has the same lifetime as the worker thread and acts as target for +// interactions that outlives individual stream, e.g. timer, async calls. +class PluginRootContext : public RootContext, + public RouteRuleMatcher { + public: + PluginRootContext(uint32_t id, std::string_view root_id) + : RootContext(id, root_id) {} + ~PluginRootContext() {} + bool onConfigure(size_t) override; + FilterHeadersStatus onHeader(const ModelRouterConfigRule&); + FilterDataStatus onBody(const ModelRouterConfigRule&, std::string_view); + bool configure(size_t); + + private: + bool parsePluginConfig(const json&, ModelRouterConfigRule&) override; +}; + +// Per-stream context. +class PluginContext : public Context { + public: + explicit PluginContext(uint32_t id, RootContext* root) : Context(id, root) {} + FilterHeadersStatus onRequestHeaders(uint32_t, bool) override; + FilterDataStatus onRequestBody(size_t, bool) override; + + private: + inline PluginRootContext* rootContext() { + return dynamic_cast(this->root()); + } + + size_t body_total_size_ = 0; + const ModelRouterConfigRule* config_ = nullptr; +}; + +#ifdef NULL_PLUGIN + +} // namespace model_router +} // namespace null_plugin +} // namespace proxy_wasm + +#endif diff --git a/plugins/wasm-cpp/extensions/model_router/plugin_test.cc b/plugins/wasm-cpp/extensions/model_router/plugin_test.cc new file mode 100644 index 0000000000..9ce5998051 --- /dev/null +++ b/plugins/wasm-cpp/extensions/model_router/plugin_test.cc @@ -0,0 +1,144 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "extensions/model_router/plugin.h" + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "include/proxy-wasm/context.h" +#include "include/proxy-wasm/null.h" + +namespace proxy_wasm { +namespace null_plugin { +namespace model_router { + +NullPluginRegistry* context_registry_; +RegisterNullVmPluginFactory register_model_router_plugin("model_router", []() { + return std::make_unique(model_router::context_registry_); +}); + +class MockContext : public proxy_wasm::ContextBase { + public: + MockContext(WasmBase* wasm) : ContextBase(wasm) {} + MOCK_METHOD(BufferInterface*, getBuffer, (WasmBufferType)); + MOCK_METHOD(WasmResult, log, (uint32_t, std::string_view)); + MOCK_METHOD(WasmResult, setBuffer, + (WasmBufferType, size_t, size_t, std::string_view)); + MOCK_METHOD(WasmResult, getHeaderMapValue, + (WasmHeaderMapType /* type */, std::string_view /* key */, + std::string_view* /*result */)); + MOCK_METHOD(WasmResult, addHeaderMapValue, + (WasmHeaderMapType, std::string_view, std::string_view)); + MOCK_METHOD(WasmResult, getProperty, (std::string_view, std::string*)); + MOCK_METHOD(WasmResult, setProperty, (std::string_view, std::string_view)); +}; +class ModelRouterTest : public ::testing::Test { + protected: + ModelRouterTest() { + // Initialize test VM + test_vm_ = createNullVm(); + wasm_base_ = std::make_unique( + std::move(test_vm_), "test-vm", "", "", + std::unordered_map{}, + AllowedCapabilitiesMap{}); + wasm_base_->load("model_router"); + wasm_base_->initialize(); + // Initialize host side context + mock_context_ = std::make_unique(wasm_base_.get()); + current_context_ = mock_context_.get(); + // Initialize Wasm sandbox context + root_context_ = std::make_unique(0, ""); + context_ = std::make_unique(1, root_context_.get()); + + ON_CALL(*mock_context_, log(testing::_, testing::_)) + .WillByDefault([](uint32_t, std::string_view m) { + std::cerr << m << "\n"; + return WasmResult::Ok; + }); + + ON_CALL(*mock_context_, getBuffer(testing::_)) + .WillByDefault([&](WasmBufferType type) { + if (type == WasmBufferType::HttpRequestBody) { + return &body_; + } + return &config_; + }); + ON_CALL(*mock_context_, getHeaderMapValue(WasmHeaderMapType::RequestHeaders, + testing::_, testing::_)) + .WillByDefault([&](WasmHeaderMapType, std::string_view header, + std::string_view* result) { + if (header == "content-type") { + *result = "application/json"; + } else if (header == "content-length") { + *result = "1024"; + } + return WasmResult::Ok; + }); + ON_CALL(*mock_context_, addHeaderMapValue(WasmHeaderMapType::RequestHeaders, + testing::_, testing::_)) + .WillByDefault([&](WasmHeaderMapType, std::string_view header, + std::string_view value) { return WasmResult::Ok; }); + ON_CALL(*mock_context_, getProperty(testing::_, testing::_)) + .WillByDefault([&](std::string_view path, std::string* result) { + *result = route_name_; + return WasmResult::Ok; + }); + ON_CALL(*mock_context_, setProperty(testing::_, testing::_)) + .WillByDefault( + [&](std::string_view, std::string_view) { return WasmResult::Ok; }); + } + ~ModelRouterTest() override {} + std::unique_ptr wasm_base_; + std::unique_ptr test_vm_; + std::unique_ptr mock_context_; + std::unique_ptr root_context_; + std::unique_ptr context_; + std::string route_name_; + BufferBase body_; + BufferBase config_; +}; + +TEST_F(ModelRouterTest, RewriteModelAndHeader) { + std::string configuration = R"( +{ + "enable": true + })"; + + config_.set(configuration); + EXPECT_TRUE(root_context_->configure(configuration.size())); + + std::string request_json = R"({"model": "qwen/qwen-long"})"; + EXPECT_CALL(*mock_context_, + setBuffer(testing::_, testing::_, testing::_, testing::_)) + .WillOnce([&](WasmBufferType, size_t, size_t, std::string_view body) { + EXPECT_EQ(body, R"({"model":"qwen-long"})"); + return WasmResult::Ok; + }); + + EXPECT_CALL( + *mock_context_, + addHeaderMapValue(testing::_, std::string_view("x-higress-llm-provider"), + std::string_view("qwen"))); + + body_.set(request_json); + EXPECT_EQ(context_->onRequestHeaders(0, false), + FilterHeadersStatus::StopIteration); + EXPECT_EQ(context_->onRequestBody(28, true), FilterDataStatus::Continue); +} + +} // namespace model_router +} // namespace null_plugin +} // namespace proxy_wasm