From bad110d0a2e0f001c96db716c9642e5adf145289 Mon Sep 17 00:00:00 2001 From: dayong Date: Fri, 27 Dec 2024 15:05:56 +0800 Subject: [PATCH 1/5] add rpc-middlewares package --- Cargo.lock | 712 ++++++++++++++++-- Cargo.toml | 3 +- crates/rpc/rpc-builder/Cargo.toml | 17 +- crates/rpc/rpc-middlewares/Cargo.toml | 31 + .../rpc-middlewares/examples/middleware.rs | 137 ++++ .../rpc-middlewares/examples/throttling.toml | 3 + crates/rpc/rpc-middlewares/src/lib.rs | 5 + crates/rpc/rpc-middlewares/src/metrics.rs | 80 ++ crates/rpc/rpc-middlewares/src/throttle.rs | 153 ++++ crates/util/throttling/src/token_bucket.rs | 2 +- 10 files changed, 1085 insertions(+), 58 deletions(-) create mode 100644 crates/rpc/rpc-middlewares/Cargo.toml create mode 100644 crates/rpc/rpc-middlewares/examples/middleware.rs create mode 100644 crates/rpc/rpc-middlewares/examples/throttling.toml create mode 100644 crates/rpc/rpc-middlewares/src/lib.rs create mode 100644 crates/rpc/rpc-middlewares/src/metrics.rs create mode 100644 crates/rpc/rpc-middlewares/src/throttle.rs diff --git a/Cargo.lock b/Cargo.lock index 52e3f4ba8a..334eb2e927 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -634,7 +634,7 @@ checksum = "baf13118df3e3dce4b5ac930641343b91b656e4e72c8f8325838b01a4b1c9d45" dependencies = [ "http 0.2.9", "log", - "url 2.3.1", + "url 2.5.4", ] [[package]] @@ -1139,6 +1139,12 @@ dependencies = [ "libc", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cexpr" version = "0.6.0" @@ -1407,17 +1413,24 @@ dependencies = [ "cfx-rpc-cfx-types", "cfx-rpc-eth-api", "cfx-rpc-eth-types", + "cfx-rpc-utils", "cfx-types", + "cfx-util-macros", "cfxcore", + "futures-util", "http 1.1.0", + "jsonrpc-core 15.1.0", "jsonrpsee", "jsonrpsee-core", "jsonrpsee-types", + "log", "pin-project", "rand 0.7.3", "serde", "strum", "thiserror", + "throttling", + "tokio 1.40.0", "tower", ] @@ -2106,6 +2119,16 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes 1.9.0", + "memchr", +] + [[package]] name = "conflux" version = "2.4.0" @@ -2220,9 +2243,9 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -2230,9 +2253,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" @@ -2459,9 +2482,9 @@ dependencies = [ [[package]] name = "crypto-mac" -version = "0.10.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" +checksum = "4857fd85a0c34b3c3297875b747c1e02e06b6a0ea32dd892d8192b9ce0813ea6" dependencies = [ "generic-array 0.14.6", "subtle", @@ -2469,9 +2492,9 @@ dependencies = [ [[package]] name = "crypto-mac" -version = "0.11.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +checksum = "25fab6889090c8133f3deb8f73ba3c65a7f456f66436fc012a1b1e272b1e103e" dependencies = [ "generic-array 0.14.6", "subtle", @@ -3066,6 +3089,17 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", +] + [[package]] name = "docopt" version = "1.1.1" @@ -3461,7 +3495,7 @@ dependencies = [ "proc-macro2 1.0.85", "quote 1.0.36", "syn 1.0.109", - "synstructure", + "synstructure 0.12.6", ] [[package]] @@ -3608,11 +3642,11 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ - "percent-encoding 2.2.0", + "percent-encoding 2.3.1", ] [[package]] @@ -3764,6 +3798,12 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + [[package]] name = "futures-util" version = "0.3.30" @@ -3879,9 +3919,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if 1.0.0", "libc", @@ -3926,7 +3966,7 @@ dependencies = [ "libc", "libgit2-sys", "log", - "url 2.3.1", + "url 2.5.4", ] [[package]] @@ -4147,7 +4187,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" dependencies = [ - "crypto-mac 0.10.1", + "crypto-mac 0.10.0", "digest 0.9.0", ] @@ -4157,7 +4197,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" dependencies = [ - "crypto-mac 0.11.1", + "crypto-mac 0.11.0", "digest 0.9.0", ] @@ -4431,6 +4471,124 @@ dependencies = [ "cxx-build", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec 1.13.2", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", +] + [[package]] name = "idna" version = "0.1.5" @@ -4444,12 +4602,23 @@ dependencies = [ [[package]] name = "idna" -version = "0.3.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec 1.13.2", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] @@ -4460,7 +4629,7 @@ checksum = "c5bab3d4e7e5b7e564770440ee64b6ae9fd227434ea8f2845ed5b5859d7ca652" dependencies = [ "attohttpc", "rand 0.7.3", - "url 2.3.1", + "url 2.5.4", "xmltree", ] @@ -4722,6 +4891,26 @@ dependencies = [ "libc", ] +[[package]] +name = "jni" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + [[package]] name = "jobserver" version = "0.1.26" @@ -4976,10 +5165,34 @@ dependencies = [ "jsonrpsee-proc-macros", "jsonrpsee-server", "jsonrpsee-types", + "jsonrpsee-ws-client", "tokio 1.40.0", "tracing", ] +[[package]] +name = "jsonrpsee-client-transport" +version = "0.24.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89841d4f03a14c055eb41d4f41901819573ef948e8ee0d5c86466fd286b2ce7f" +dependencies = [ + "base64 0.22.1", + "futures-util", + "http 1.1.0", + "jsonrpsee-core", + "pin-project", + "rustls 0.23.20", + "rustls-pki-types", + "rustls-platform-verifier", + "soketto", + "thiserror", + "tokio 1.40.0", + "tokio-rustls", + "tokio-util 0.7.7", + "tracing", + "url 2.5.4", +] + [[package]] name = "jsonrpsee-core" version = "0.24.4" @@ -4988,18 +5201,21 @@ checksum = "ff79651479f69ada7bda604ef2acf3f1aa50755d97cc36d25ff04c2664f9d96f" dependencies = [ "async-trait", "bytes 1.9.0", + "futures-timer", "futures-util", "http 1.1.0", "http-body 1.0.1", "http-body-util", "jsonrpsee-types", "parking_lot 0.12.1", + "pin-project", "rand 0.8.5", "rustc-hash 2.0.0", "serde", "serde_json", "thiserror", "tokio 1.40.0", + "tokio-stream", "tracing", ] @@ -5055,6 +5271,19 @@ dependencies = [ "thiserror", ] +[[package]] +name = "jsonrpsee-ws-client" +version = "0.24.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87bc869e143d430e748988261d19b630e8f1692074e68f1a7f0eb4c521d2fc58" +dependencies = [ + "http 1.1.0", + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-types", + "url 2.5.4", +] + [[package]] name = "k256" version = "0.13.3" @@ -5142,7 +5371,7 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" dependencies = [ - "spin", + "spin 0.5.2", ] [[package]] @@ -5153,9 +5382,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.151" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libflate" @@ -5292,6 +5521,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + [[package]] name = "lock_api" version = "0.3.4" @@ -5410,7 +5645,7 @@ version = "0.1.1" dependencies = [ "proc-macro2 1.0.85", "syn 1.0.109", - "synstructure", + "synstructure 0.12.6", ] [[package]] @@ -5419,6 +5654,15 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata", +] + [[package]] name = "matches" version = "0.1.10" @@ -6133,7 +6377,7 @@ checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" dependencies = [ "proc-macro2 1.0.85", "syn 1.0.109", - "synstructure", + "synstructure 0.12.6", ] [[package]] @@ -6170,7 +6414,7 @@ dependencies = [ "rand 0.7.3", "sha-1", "slab", - "url 2.3.1", + "url 2.5.4", ] [[package]] @@ -6320,7 +6564,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3b8c0d71734018084da0c0354193a5edfb81b20d2d57a92c5b154aefc554a4a" dependencies = [ - "crypto-mac 0.10.1", + "crypto-mac 0.10.0", ] [[package]] @@ -6330,7 +6574,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf916dd32dd26297907890d99dc2740e33f6bd9073965af4ccff2967962f5508" dependencies = [ "base64ct", - "crypto-mac 0.10.1", + "crypto-mac 0.10.0", "hmac 0.10.1", "password-hash", "sha2 0.9.9", @@ -6342,7 +6586,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f05894bce6a1ba4be299d0c5f29563e08af2bc18bb7d48313113bed71e904739" dependencies = [ - "crypto-mac 0.11.1", + "crypto-mac 0.11.0", ] [[package]] @@ -6368,9 +6612,9 @@ checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" @@ -6794,7 +7038,7 @@ dependencies = [ "fnv", "lazy_static", "quick-error", - "spin", + "spin 0.5.2", ] [[package]] @@ -6855,7 +7099,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" dependencies = [ - "percent-encoding 2.2.0", + "percent-encoding 2.3.1", ] [[package]] @@ -7016,7 +7260,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.8", + "getrandom 0.2.15", ] [[package]] @@ -7174,7 +7418,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.8", + "getrandom 0.2.15", "redox_syscall 0.2.16", "thiserror", ] @@ -7216,6 +7460,15 @@ dependencies = [ "regex-syntax 0.6.29", ] +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + [[package]] name = "regex-syntax" version = "0.6.29" @@ -7259,7 +7512,7 @@ dependencies = [ "mime", "native-tls", "once_cell", - "percent-encoding 2.2.0", + "percent-encoding 2.3.1", "pin-project-lite 0.2.14", "serde", "serde_json", @@ -7267,7 +7520,7 @@ dependencies = [ "tokio 1.40.0", "tokio-native-tls", "tower-service", - "url 2.3.1", + "url 2.5.4", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -7347,6 +7600,21 @@ dependencies = [ "subtle", ] +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if 1.0.0", + "getrandom 0.2.15", + "libc", + "spin 0.9.8", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "ripemd" version = "0.1.3" @@ -7427,6 +7695,28 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "rpc-middlewares" +version = "2.4.0" +dependencies = [ + "anyhow", + "cfx-rpc-utils", + "cfx-util-macros", + "futures 0.3.30", + "futures-util", + "jsonrpc-core 15.1.0", + "jsonrpsee", + "jsonrpsee-types", + "lazy_static", + "log", + "metrics", + "parking_lot 0.11.2", + "rustls 0.21.12", + "throttling", + "tokio 1.40.0", + "tracing-subscriber", +] + [[package]] name = "ruint" version = "1.12.3" @@ -7540,6 +7830,109 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki 0.101.7", + "sct", +] + +[[package]] +name = "rustls" +version = "0.23.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki 0.102.8", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" + +[[package]] +name = "rustls-platform-verifier" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afbb878bdfdf63a336a5e63561b1835e7a8c91524f51621db870169eac84b490" +dependencies = [ + "core-foundation", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls 0.23.20", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki 0.102.8", + "security-framework", + "security-framework-sys", + "webpki-roots", + "winapi 0.3.9", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.12" @@ -7703,6 +8096,16 @@ dependencies = [ "sha2 0.10.6", ] +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "sec1" version = "0.7.1" @@ -7770,22 +8173,23 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.8.2" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", "core-foundation", "core-foundation-sys", "libc", + "num-bigint 0.4.4", "security-framework-sys", ] [[package]] name = "security-framework-sys" -version = "2.8.0" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" dependencies = [ "core-foundation-sys", "libc", @@ -8036,6 +8440,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shell32-sys" version = "0.1.2" @@ -8203,6 +8616,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "spki" version = "0.4.1" @@ -8251,6 +8670,12 @@ dependencies = [ "sqlite3-src", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -8360,9 +8785,9 @@ dependencies = [ [[package]] name = "subtle" -version = "2.4.1" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" @@ -8421,6 +8846,17 @@ dependencies = [ "unicode-xid 0.2.4", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", +] + [[package]] name = "sysinfo" version = "0.27.8" @@ -8528,6 +8964,16 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", +] + [[package]] name = "threadpool" version = "1.8.1" @@ -8612,6 +9058,16 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinytemplate" version = "1.2.1" @@ -8818,6 +9274,16 @@ dependencies = [ "tokio-sync", ] +[[package]] +name = "tokio-rustls" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" +dependencies = [ + "rustls 0.23.20", + "tokio 1.40.0", +] + [[package]] name = "tokio-service" version = "0.1.0" @@ -9079,6 +9545,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cf865b5ddc38e503a29c41c4843e616a73028ae18c637bc3eb2afaef4909c84" +dependencies = [ + "ansi_term", + "lazy_static", + "matchers", + "regex", + "sharded-slab", + "smallvec 1.13.2", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -9254,9 +9750,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "universal-hash" -version = "0.4.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +checksum = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402" dependencies = [ "generic-array 0.14.6", "subtle", @@ -9281,6 +9777,12 @@ dependencies = [ "destructure_traitobject", ] +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "ureq" version = "1.5.5" @@ -9295,7 +9797,7 @@ dependencies = [ "qstring", "serde", "serde_json", - "url 2.3.1", + "url 2.5.4", ] [[package]] @@ -9311,15 +9813,27 @@ dependencies = [ [[package]] name = "url" -version = "2.3.1" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", - "idna 0.3.0", - "percent-encoding 2.2.0", + "idna 1.0.3", + "percent-encoding 2.3.1", ] +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -9513,6 +10027,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-roots" +version = "0.26.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "winapi" version = "0.2.8" @@ -9764,6 +10287,18 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "ws2_32-sys" version = "0.2.1" @@ -9839,6 +10374,30 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", + "synstructure 0.13.1", +] + [[package]] name = "zerocopy" version = "0.7.32" @@ -9859,6 +10418,27 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", + "synstructure 0.13.1", +] + [[package]] name = "zeroize" version = "1.7.0" @@ -9877,7 +10457,29 @@ dependencies = [ "proc-macro2 1.0.85", "quote 1.0.36", "syn 1.0.109", - "synstructure", + "synstructure 0.12.6", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index dbe78c3fcc..d9220e71f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -101,7 +101,8 @@ members = [ "crates/pos/types/move-core-types", "crates/pos/types/pow-types", "crates/pos/types/types", - "crates/pos/config/management/network-address-encryption", + "crates/pos/config/management/network-address-encryption", + "crates/rpc/rpc-middlewares", ] resolver = "2" diff --git a/crates/rpc/rpc-builder/Cargo.toml b/crates/rpc/rpc-builder/Cargo.toml index 06dfaa44f2..fc92cbb079 100644 --- a/crates/rpc/rpc-builder/Cargo.toml +++ b/crates/rpc/rpc-builder/Cargo.toml @@ -18,6 +18,7 @@ strum = { workspace = true, features = ["derive"] } alloy-primitives = { workspace = true} thiserror = { workspace = true } +jsonrpc-core ={ workspace = true} jsonrpsee-core = { workspace = true } jsonrpsee-types = { workspace = true } jsonrpsee = { workspace = true, features = ["server"] } @@ -31,4 +32,18 @@ cfx-rpc-eth-api = { workspace = true } cfxcore = { workspace = true } cfx-types = { workspace = true } cfx-rpc-eth-types = { workspace = true } -cfx-rpc-cfx-types = { workspace = true } \ No newline at end of file +cfx-rpc-cfx-types = { workspace = true } +throttling = { workspace = true } +cfx-util-macros = { workspace = true } +log = { workspace = true } +cfx-rpc-utils = { workspace = true } + +[dev-dependencies] +tokio = { workspace = true, features = ["full"] } + +[dependencies.futures-util] +version = "0.3.14" +features = [ + "io", + "async-await-macro", +] \ No newline at end of file diff --git a/crates/rpc/rpc-middlewares/Cargo.toml b/crates/rpc/rpc-middlewares/Cargo.toml new file mode 100644 index 0000000000..7098f10fb6 --- /dev/null +++ b/crates/rpc/rpc-middlewares/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "rpc-middlewares" +edition = "2021" +version.workspace = true +authors.workspace = true +description.workspace = true +documentation.workspace = true +homepage.workspace = true +keywords.workspace = true +repository.workspace = true +license-file.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +jsonrpc-core = { workspace = true } +jsonrpsee = { workspace = true ,features = ["server","ws-client"]} +rustls = { version = "0.21", features = ["dangerous_configuration"] } +throttling = { workspace = true } +futures-util = { workspace = true } +log = { workspace = true } +jsonrpsee-types = { workspace = true } +cfx-rpc-utils = { workspace = true } +cfx-util-macros = { workspace = true } +lazy_static = { workspace = true } +parking_lot = { workspace = true } +metrics = { workspace = true } +futures = { workspace = true } +tracing-subscriber = {version = "=0.3.0",features = ["env-filter"]} +anyhow = {workspace = true} +tokio = { workspace = true } diff --git a/crates/rpc/rpc-middlewares/examples/middleware.rs b/crates/rpc/rpc-middlewares/examples/middleware.rs new file mode 100644 index 0000000000..421bfbeb29 --- /dev/null +++ b/crates/rpc/rpc-middlewares/examples/middleware.rs @@ -0,0 +1,137 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any +// person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the +// Software without restriction, including without +// limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +//! jsonrpsee supports two kinds of middlewares `http_middleware` and +//! `rpc_middleware`. +//! +//! This example demonstrates how to use the `rpc_middleware` which applies for +//! each JSON-RPC method call and batch requests may call the middleware more +//! than once. +//! +//! A typical use-case for this is to implement rate-limiting based on the +//! actual number of JSON-RPC methods calls and a request could potentially be +//! made by HTTP or WebSocket which this middleware is agnostic to. +//! +//! Contrary the HTTP middleware does only apply per HTTP request and +//! may be handy in some scenarios such CORS but if you want to access +//! to the actual JSON-RPC details this is the middleware to use. + +use std::net::SocketAddr; + +use jsonrpsee::{ + core::client::ClientT, + rpc_params, + server::{ + middleware::rpc::{RpcServiceBuilder, RpcServiceT}, + RpcModule, Server, + }, + types::Request, + ws_client::WsClientBuilder, +}; +use log::debug; +use rpc_middlewares::{Metrics, Throttle}; + +#[derive(Clone)] +pub struct Logger(S); + +impl<'a, S> RpcServiceT<'a> for Logger +where S: RpcServiceT<'a> + Send + Sync +{ + type Future = S::Future; + + fn call(&self, req: Request<'a>) -> Self::Future { + println!("logger middleware: method `{}`", req.method); + self.0.call(req) + } +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + tracing_subscriber::FmtSubscriber::builder() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .try_init() + .expect("setting default subscriber failed"); + + let addr = run_server().await?; + let url = format!("ws://{}", addr); + + for _ in 0..10 { + let client: jsonrpsee::ws_client::WsClient = + WsClientBuilder::default().build(&url).await?; + let response: String = + client.request("say_hello", rpc_params![]).await?; + println!("response: {:?}", response); + let _response: Result = + client.request("unknown_method", rpc_params![]).await; + let _: String = client.request("say_hello", rpc_params![]).await?; + let _: String = client.request("thready", rpc_params![4]).await?; + + tokio::time::sleep(std::time::Duration::from_millis(100)).await; + } + + Ok(()) +} + +async fn run_server() -> anyhow::Result { + let config_path = std::env::current_dir()?.join("throttling.toml"); + + debug!("throttling config path: {:?}", config_path); + + let rpc_middleware = RpcServiceBuilder::new() + // .layer_fn(|s| + // ThrottleInterceptor::new(Some("/Users/dayong/myspace/mywork/ + // conflux-rust/crates/rpc/rpc-middlewares/src/throttling.toml"), + // "test",s)) + .layer_fn(move |s| { + Throttle::new(Some(config_path.to_str().unwrap()), "test", s) + }) + .layer_fn(|s| Metrics::new(s)); + + let server = Server::builder() + .set_rpc_middleware(rpc_middleware) + .build("127.0.0.1:0") + .await?; + let mut module = RpcModule::new(()); + module.register_method("say_hello", |_, _, _| "lo")?; + module.register_method("thready", |params, _, _| { + let thread_count: usize = params.one().unwrap(); + for _ in 0..thread_count { + std::thread::spawn(|| { + std::thread::sleep(std::time::Duration::from_secs(1)) + }); + } + "" + })?; + let addr = server.local_addr()?; + let handle = server.start(module); + + // In this example we don't care about doing shutdown so let's it run + // forever. You may use the `ServerHandle` to shut it down or manage it + // yourself. + tokio::spawn(handle.stopped()); + + Ok(addr) +} diff --git a/crates/rpc/rpc-middlewares/examples/throttling.toml b/crates/rpc/rpc-middlewares/examples/throttling.toml new file mode 100644 index 0000000000..8a277df5a0 --- /dev/null +++ b/crates/rpc/rpc-middlewares/examples/throttling.toml @@ -0,0 +1,3 @@ +[test] +say_hello="1,1,1,1,1" +say_goodbye="1,1,1,1,1" \ No newline at end of file diff --git a/crates/rpc/rpc-middlewares/src/lib.rs b/crates/rpc/rpc-middlewares/src/lib.rs new file mode 100644 index 0000000000..b793108775 --- /dev/null +++ b/crates/rpc/rpc-middlewares/src/lib.rs @@ -0,0 +1,5 @@ +mod metrics; +mod throttle; + +pub use metrics::Metrics; +pub use throttle::Throttle; diff --git a/crates/rpc/rpc-middlewares/src/metrics.rs b/crates/rpc/rpc-middlewares/src/metrics.rs new file mode 100644 index 0000000000..6728b92a66 --- /dev/null +++ b/crates/rpc/rpc-middlewares/src/metrics.rs @@ -0,0 +1,80 @@ +use cfx_rpc_utils::error::jsonrpsee_error_helpers::jsonrpc_error_to_error_object_owned; +use futures::{future::lazy, FutureExt}; +use futures_util::future::BoxFuture; +use jsonrpc_core::Result as RpcResult; +use jsonrpsee::server::{middleware::rpc::RpcServiceT, MethodResponse}; +use jsonrpsee_types::Request; +use lazy_static::lazy_static; +use log::debug; +use metrics::{register_timer_with_group, ScopeTimer, Timer}; +use parking_lot::Mutex; +use std::{collections::HashMap, sync::Arc}; + +lazy_static! { + static ref METRICS_INTERCEPTOR_TIMERS: Mutex>> = + Default::default(); +} +#[derive(Clone)] +pub struct Metrics { + service: S, +} + +impl Metrics { + pub fn new(service: S) -> Self { Self { service } } +} + +impl<'a, S> Metrics { + fn before(&self, name: &String) -> RpcResult<()> { + // Use a global variable here because `http` and `web3` setup different + // interceptors for the same RPC API. + let mut timers = METRICS_INTERCEPTOR_TIMERS.lock(); + if !timers.contains_key(name) { + let timer = register_timer_with_group("rpc", name.as_str()); + timers.insert(name.clone(), timer); + } + Ok(()) + } +} + +impl<'a, S> RpcServiceT<'a> for Metrics +where S: RpcServiceT<'a> + Send + Sync + Clone + 'static +{ + type Future = BoxFuture<'a, MethodResponse>; + + fn call(&self, req: Request<'a>) -> Self::Future { + let before_result = self.before(&req.method_name().to_string()); + + debug!("run metrics interceptor before_result: {:?}", before_result); + match before_result { + Ok(_) => { + let maybe_timer = METRICS_INTERCEPTOR_TIMERS + .lock() + .get(req.method_name()) + .map(|timer| timer.clone()); + let setup = lazy(move |_| { + Ok(maybe_timer + .as_ref() + .map(|timer| ScopeTimer::time_scope(timer.clone()))) + }); + + let service = self.service.clone(); + Box::pin(async move { + let timer: Result<_, ()> = setup.await; + let resp = service.call(req).await; + drop(timer); + resp + }) + .boxed() + } + Err(e) => { + return Box::pin(async move { + MethodResponse::error( + req.id, + jsonrpc_error_to_error_object_owned(e), + ) + }) + .boxed() + } + } + } +} diff --git a/crates/rpc/rpc-middlewares/src/throttle.rs b/crates/rpc/rpc-middlewares/src/throttle.rs new file mode 100644 index 0000000000..d2ed6e1c54 --- /dev/null +++ b/crates/rpc/rpc-middlewares/src/throttle.rs @@ -0,0 +1,153 @@ +use cfx_rpc_utils::error::{ + jsonrpc_error_helpers::request_rejected_too_many_request_error, + jsonrpsee_error_helpers::jsonrpc_error_to_error_object_owned, +}; +use cfx_util_macros::bail; +use futures::FutureExt; +use futures_util::future::BoxFuture; +use jsonrpc_core::Result as RpcResult; +use jsonrpsee::server::{middleware::rpc::RpcServiceT, MethodResponse}; +use jsonrpsee_types::Request; +use log::debug; +use throttling::token_bucket::{ThrottleResult, TokenBucketManager}; + +#[derive(Clone)] +pub struct Throttle { + service: S, + manager: TokenBucketManager, +} + +impl Throttle { + pub fn new(file: Option<&str>, section: &str, s: S) -> Self { + let manager = match file { + Some(file) => TokenBucketManager::load(file, Some(section)) + .expect("invalid throttling configuration file"), + None => TokenBucketManager::default(), + }; + + Throttle { + service: s, + manager, + } + } + + pub fn before(&self, name: &String) -> RpcResult<()> { + let bucket = match self.manager.get(name) { + Some(bucket) => bucket, + None => return Ok(()), + }; + + let result = bucket.lock().throttle_default(); + + match result { + ThrottleResult::Success => Ok(()), + ThrottleResult::Throttled(wait_time) => { + debug!("RPC {} throttled in {:?}", name, wait_time); + bail!(request_rejected_too_many_request_error(Some(format!( + "throttled in {:?}", + wait_time + )))) + } + ThrottleResult::AlreadyThrottled => { + debug!("RPC {} already throttled", name); + bail!(request_rejected_too_many_request_error(Some( + "already throttled, please try again later".into() + ))) + } + } + } +} + +impl<'a, S> RpcServiceT<'a> for Throttle +where S: RpcServiceT<'a> + Send + Sync + Clone + 'static +{ + type Future = BoxFuture<'a, MethodResponse>; + + fn call(&self, req: Request<'a>) -> Self::Future { + let service = self.service.clone(); + let throlltle_result = self.before(&req.method_name().to_string()); + // Box::pin(async move { service.call(req).await }) + match throlltle_result { + Ok(_) => { + debug!("throttle interceptor: method `{}` success", req.method); + Box::pin(async move { service.call(req).await }).boxed() + } + Err(e) => { + debug!("throttle interceptor: method `{}` failed", req.method); + Box::pin(async move { + MethodResponse::error( + req.id, + jsonrpc_error_to_error_object_owned(e), + ) + }) + .boxed() + } + } + } +} + +#[cfg(test)] +mod tests { + use jsonrpsee::{ + core::client::ClientT, + rpc_params, + server::{RpcServiceBuilder, Server}, + tokio, + ws_client::WsClientBuilder, + RpcModule, + }; + use std::net::SocketAddr; + + use super::*; + + async fn run_server() -> anyhow::Result { + let m = RpcServiceBuilder::new().layer_fn(|s: jsonrpsee::server::middleware::rpc::RpcService| { + Throttle::new(Some("/Users/dayong/myspace/mywork/conflux-rust/crates/rpc/rpc-middlewares/src/throttling.toml"), "test", s) + }); + + let server = Server::builder() + .set_rpc_middleware(m) + .build("127.0.0.1:0") + .await + .unwrap(); + + let addr = server.local_addr()?; + + let mut module = RpcModule::new(()); + module.register_method("say_hello", |_, _, _| "lo").unwrap(); + module + .register_method("say_goodbye", |_, _, _| "goodbye") + .unwrap(); + + let handle = server.start(module); + tokio::spawn(handle.stopped()); + + Ok(addr) + } + + async fn call_rpc(addr: SocketAddr) { + tracing_subscriber::FmtSubscriber::builder() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .try_init() + .expect("setting default subscriber failed"); + + let url = format!("ws://{}", addr); + let client = WsClientBuilder::default().build(url).await.unwrap(); + + for _ in 0..30 { + let resp_hello: String = + client.request("say_hello", rpc_params![]).await.unwrap(); + println!("resp_hello: {:?}", resp_hello); + let resp_goodbye: String = + client.request("say_goodbye", rpc_params![]).await.unwrap(); + println!("resp_goodbye: {:?}", resp_goodbye); + } + tokio::time::sleep(std::time::Duration::from_millis(100)).await; + } + + #[tokio::test] + async fn test_throttle() { + let addr = run_server().await.unwrap(); + call_rpc(addr).await; + } +} diff --git a/crates/util/throttling/src/token_bucket.rs b/crates/util/throttling/src/token_bucket.rs index 8918ea340e..5ebd248390 100644 --- a/crates/util/throttling/src/token_bucket.rs +++ b/crates/util/throttling/src/token_bucket.rs @@ -252,7 +252,7 @@ impl FromStr for TokenBucket { } } -#[derive(Default, DeriveMallocSizeOf)] +#[derive(Default, DeriveMallocSizeOf, Clone)] pub struct TokenBucketManager { // manage buckets by name buckets: HashMap>>, From 965a7505a22c4de78fa204f5914f7e6c92d1ae81 Mon Sep 17 00:00:00 2001 From: dayong Date: Fri, 27 Dec 2024 18:00:57 +0800 Subject: [PATCH 2/5] set rpc middleware when start async rpcs --- Cargo.lock | 45 ++--- Cargo.toml | 1 + crates/client/src/common/mod.rs | 1 + crates/client/src/rpc.rs | 13 +- crates/rpc/rpc-builder/Cargo.toml | 1 + crates/rpc/rpc-builder/src/lib.rs | 165 ++++++++---------- crates/rpc/rpc-middlewares/Cargo.toml | 2 +- .../rpc-middlewares/examples/middleware.rs | 6 +- 8 files changed, 112 insertions(+), 122 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 334eb2e927..2d8c49582e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1413,6 +1413,7 @@ dependencies = [ "cfx-rpc-cfx-types", "cfx-rpc-eth-api", "cfx-rpc-eth-types", + "cfx-rpc-middlewares", "cfx-rpc-utils", "cfx-types", "cfx-util-macros", @@ -1503,6 +1504,28 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cfx-rpc-middlewares" +version = "2.4.0" +dependencies = [ + "anyhow", + "cfx-rpc-utils", + "cfx-util-macros", + "futures 0.3.30", + "futures-util", + "jsonrpc-core 15.1.0", + "jsonrpsee", + "jsonrpsee-types", + "lazy_static", + "log", + "metrics", + "parking_lot 0.11.2", + "rustls 0.21.12", + "throttling", + "tokio 1.40.0", + "tracing-subscriber", +] + [[package]] name = "cfx-rpc-primitives" version = "2.4.0" @@ -7695,28 +7718,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "rpc-middlewares" -version = "2.4.0" -dependencies = [ - "anyhow", - "cfx-rpc-utils", - "cfx-util-macros", - "futures 0.3.30", - "futures-util", - "jsonrpc-core 15.1.0", - "jsonrpsee", - "jsonrpsee-types", - "lazy_static", - "log", - "metrics", - "parking_lot 0.11.2", - "rustls 0.21.12", - "throttling", - "tokio 1.40.0", - "tracing-subscriber", -] - [[package]] name = "ruint" version = "1.12.3" diff --git a/Cargo.toml b/Cargo.toml index d9220e71f9..0d0c1e04e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -197,6 +197,7 @@ cfx-rpc = { path = "./crates/rpc/rpc-eth-impl" } cfx-rpc-utils = { path = "./crates/rpc/rpc-utils" } cfx-rpc-builder = { path = "./crates/rpc/rpc-builder" } cfx-rpc-cfx-impl = { path = "./crates/rpc/rpc-cfx-impl" } +cfx-rpc-middlewares = { path = "./crates/rpc/rpc-middlewares" } bounded-executor = { path = "./crates/pos/common/bounded-executor" } diem-channel = { path = "./crates/pos/common/channel", package = "channel" } channel = { path = "./crates/pos/common/channel" } diff --git a/crates/client/src/common/mod.rs b/crates/client/src/common/mod.rs index a0741b1ae4..efa0548e94 100644 --- a/crates/client/src/common/mod.rs +++ b/crates/client/src/common/mod.rs @@ -775,6 +775,7 @@ pub fn initialize_not_light_node_modules( let async_eth_rpc_http_server = tokio_runtime.block_on(launch_async_rpc_servers( conf.rpc_impl_config(), + conf.raw_conf.throttling_conf.clone(), conf.raw_conf.public_evm_rpc_async_apis.clone(), consensus.clone(), sync.clone(), diff --git a/crates/client/src/rpc.rs b/crates/client/src/rpc.rs index e0ecce4eb7..00dd6be73e 100644 --- a/crates/client/src/rpc.rs +++ b/crates/client/src/rpc.rs @@ -514,16 +514,19 @@ where // start espace rpc server v2(async) pub async fn launch_async_rpc_servers( - config: RpcImplConfiguration, apis: RpcModuleSelection, - consensus: SharedConsensusGraph, sync: SharedSynchronizationService, - tx_pool: SharedTransactionPool, addr: Option, + rpc_conf: RpcImplConfiguration, throttling_conf_file: Option, + apis: RpcModuleSelection, consensus: SharedConsensusGraph, + sync: SharedSynchronizationService, tx_pool: SharedTransactionPool, + addr: Option, ) -> Result, String> { if addr.is_none() { return Ok(None); } + let enable_metrics = rpc_conf.enable_metrics; + let rpc_module_builder = - RpcModuleBuilder::new(config, consensus, sync, tx_pool); + RpcModuleBuilder::new(rpc_conf, consensus, sync, tx_pool); info!( "Enabled evm async rpc modules: {:?}", @@ -541,7 +544,7 @@ pub async fn launch_async_rpc_servers( .with_http_address(addr.unwrap()); let server_handle = server_config - .start(&transport_rpc_modules) + .start(&transport_rpc_modules, throttling_conf_file, enable_metrics) .await .map_err(|e| e.to_string())?; diff --git a/crates/rpc/rpc-builder/Cargo.toml b/crates/rpc/rpc-builder/Cargo.toml index fc92cbb079..dae0f15c17 100644 --- a/crates/rpc/rpc-builder/Cargo.toml +++ b/crates/rpc/rpc-builder/Cargo.toml @@ -37,6 +37,7 @@ throttling = { workspace = true } cfx-util-macros = { workspace = true } log = { workspace = true } cfx-rpc-utils = { workspace = true } +cfx-rpc-middlewares = { workspace = true } [dev-dependencies] tokio = { workspace = true, features = ["full"] } diff --git a/crates/rpc/rpc-builder/src/lib.rs b/crates/rpc/rpc-builder/src/lib.rs index 02e32cc7cc..2c3d461b37 100644 --- a/crates/rpc/rpc-builder/src/lib.rs +++ b/crates/rpc/rpc-builder/src/lib.rs @@ -32,8 +32,10 @@ mod id_provider; mod module; mod result; +use cfx_rpc_middlewares::{Metrics, Throttle}; pub use error::*; pub use id_provider::EthSubscriptionIdProvider; +use log::debug; pub use module::{EthRpcModule, RpcModuleSelection}; pub use result::*; @@ -269,7 +271,7 @@ impl RpcRegistryInner { /// started, See also [`ServerBuilder::build`] and /// [`Server::start`](jsonrpsee::server::Server::start). #[derive(Debug)] -pub struct RpcServerConfig { +pub struct RpcServerConfig { /// Configs for JSON-RPC Http. http_server_config: Option>, /// Allowed CORS Domains for http @@ -282,12 +284,12 @@ pub struct RpcServerConfig { ws_cors_domains: Option, /// Address where to bind the ws server to ws_addr: Option, - /// Configurable RPC middleware - #[allow(dead_code)] - rpc_middleware: RpcServiceBuilder, + // /// Configurable RPC middleware + // #[allow(dead_code)] + // rpc_middleware: RpcServiceBuilder, } -impl Default for RpcServerConfig { +impl Default for RpcServerConfig { fn default() -> Self { Self { http_server_config: None, @@ -296,7 +298,7 @@ impl Default for RpcServerConfig { ws_server_config: None, ws_cors_domains: None, ws_addr: None, - rpc_middleware: RpcServiceBuilder::new(), + // rpc_middleware: RpcServiceBuilder::new(), } } } @@ -339,21 +341,21 @@ impl RpcServerConfig { } } -impl RpcServerConfig { +impl RpcServerConfig { /// Configure rpc middleware - pub fn set_rpc_middleware( - self, rpc_middleware: RpcServiceBuilder, - ) -> RpcServerConfig { - RpcServerConfig { - http_server_config: self.http_server_config, - http_cors_domains: self.http_cors_domains, - http_addr: self.http_addr, - ws_server_config: self.ws_server_config, - ws_cors_domains: self.ws_cors_domains, - ws_addr: self.ws_addr, - rpc_middleware, - } - } + // pub fn set_rpc_middleware( + // self, rpc_middleware: RpcServiceBuilder, + // ) -> RpcServerConfig { + // RpcServerConfig { + // http_server_config: self.http_server_config, + // http_cors_domains: self.http_cors_domains, + // http_addr: self.http_addr, + // ws_server_config: self.ws_server_config, + // ws_cors_domains: self.ws_cors_domains, + // ws_addr: self.ws_addr, + // rpc_middleware, + // } + // } /// Configure the cors domains for http _and_ ws pub fn with_cors(self, cors_domain: Option) -> Self { @@ -428,9 +430,20 @@ impl RpcServerConfig { // Returns the [`RpcServerHandle`] with the handle to the started servers. pub async fn start( self, modules: &TransportRpcModules, + throttling_conf_file: Option, enable_metrics: bool, ) -> Result { - let mut http_handle = None; - let mut ws_handle = None; + // TODO: handle enable metrics + debug!("enable metrics: {}", enable_metrics); + + let rpc_middleware = RpcServiceBuilder::new() + .layer_fn(move |s| { + Throttle::new( + throttling_conf_file.as_ref().map(|s| s.as_str()), + "rpc", + s, + ) + }) + .layer_fn(|s| Metrics::new(s)); let http_socket_addr = self.http_addr.unwrap_or(SocketAddr::V4(SocketAddrV4::new( @@ -470,21 +483,7 @@ impl RpcServerConfig { if let Some(builder) = self.http_server_config { let server = builder - // .set_http_middleware( - // tower::ServiceBuilder::new() - // .option_layer(Self::maybe_cors_layer(cors)?) - // .option_layer(Self::maybe_jwt_layer(self. - // jwt_secret)), ) - // .set_rpc_middleware( - // self.rpc_middleware.clone().layer( - // modules - // .http - // .as_ref() - // .or(modules.ws.as_ref()) - // .map(RpcRequestMetrics::same_port) - // .unwrap_or_default(), - // ), - // ) + .set_rpc_middleware(rpc_middleware) .build(http_socket_addr) .await .map_err(|err| { @@ -503,38 +502,27 @@ impl RpcServerConfig { modules.http.as_ref().or(modules.ws.as_ref()) { let handle = server.start(module.clone()); - http_handle = Some(handle.clone()); - ws_handle = Some(handle); + let http_handle = Some(handle.clone()); + let ws_handle = Some(handle); + + return Ok(RpcServerHandle { + http_local_addr: Some(addr), + ws_local_addr: Some(addr), + http: http_handle, + ws: ws_handle, + }); } - return Ok(RpcServerHandle { - http_local_addr: Some(addr), - ws_local_addr: Some(addr), - http: http_handle, - ws: ws_handle, - }); + + return Err(RpcError::Custom( + "No valid RpcModule found from modules".to_string(), + )); } } - let mut ws_local_addr = None; - let mut ws_server = None; - let mut http_local_addr = None; - let mut http_server = None; - if let Some(builder) = self.ws_server_config { let server = builder .ws_only() - // .set_http_middleware( - // tower::ServiceBuilder::new() - // .option_layer(Self::maybe_cors_layer(self. - // ws_cors_domains.clone())?) - // .option_layer(Self::maybe_jwt_layer(self. - // jwt_secret)), ) - // .set_rpc_middleware( - // self.rpc_middleware - // .clone() - // .layer(modules.ws.as_ref(). - // map(RpcRequestMetrics::ws).unwrap_or_default()), - // ) + .set_rpc_middleware(rpc_middleware) .build(ws_socket_addr) .await .map_err(|err| { @@ -545,24 +533,23 @@ impl RpcServerConfig { RpcError::server_error(err, ServerKind::WS(ws_socket_addr)) })?; - ws_local_addr = Some(addr); - ws_server = Some(server); + let ws_local_addr = Some(addr); + let ws_server = Some(server); + let ws_handle = ws_server.map(|ws_server| { + ws_server.start(modules.ws.clone().expect("ws server error")) + }); + return Ok(RpcServerHandle { + http_local_addr: None, + ws_local_addr, + http: None, + ws: ws_handle, + }); } if let Some(builder) = self.http_server_config { let server = builder .http_only() - // .set_http_middleware( - // tower::ServiceBuilder::new() - // .option_layer(Self::maybe_cors_layer(self. - // http_cors_domains.clone())?) - // .option_layer(Self::maybe_jwt_layer(self. - // jwt_secret)), ) - // .set_rpc_middleware( - // self.rpc_middleware.clone().layer( - // modules.http.as_ref().map(RpcRequestMetrics::http). - // unwrap_or_default(), ), - // ) + .set_rpc_middleware(rpc_middleware) .build(http_socket_addr) .await .map_err(|err| { @@ -574,22 +561,22 @@ impl RpcServerConfig { let local_addr = server.local_addr().map_err(|err| { RpcError::server_error(err, ServerKind::Http(http_socket_addr)) })?; - http_local_addr = Some(local_addr); - http_server = Some(server); + let http_local_addr = Some(local_addr); + let http_server = Some(server); + let http_handle = http_server.map(|http_server| { + http_server + .start(modules.http.clone().expect("http server error")) + }); + + return Ok(RpcServerHandle { + http_local_addr, + ws_local_addr: None, + http: http_handle, + ws: None, + }); } - http_handle = http_server.map(|http_server| { - http_server.start(modules.http.clone().expect("http server error")) - }); - ws_handle = ws_server.map(|ws_server| { - ws_server.start(modules.ws.clone().expect("ws server error")) - }); - Ok(RpcServerHandle { - http_local_addr, - ws_local_addr, - http: http_handle, - ws: ws_handle, - }) + Err(RpcError::Custom("No valid server configured".to_string())) } } diff --git a/crates/rpc/rpc-middlewares/Cargo.toml b/crates/rpc/rpc-middlewares/Cargo.toml index 7098f10fb6..9d37cf4d77 100644 --- a/crates/rpc/rpc-middlewares/Cargo.toml +++ b/crates/rpc/rpc-middlewares/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "rpc-middlewares" +name = "cfx-rpc-middlewares" edition = "2021" version.workspace = true authors.workspace = true diff --git a/crates/rpc/rpc-middlewares/examples/middleware.rs b/crates/rpc/rpc-middlewares/examples/middleware.rs index 421bfbeb29..f900432241 100644 --- a/crates/rpc/rpc-middlewares/examples/middleware.rs +++ b/crates/rpc/rpc-middlewares/examples/middleware.rs @@ -41,6 +41,7 @@ use std::net::SocketAddr; +use cfx_rpc_middlewares::{Metrics, Throttle}; use jsonrpsee::{ core::client::ClientT, rpc_params, @@ -52,7 +53,6 @@ use jsonrpsee::{ ws_client::WsClientBuilder, }; use log::debug; -use rpc_middlewares::{Metrics, Throttle}; #[derive(Clone)] pub struct Logger(S); @@ -101,10 +101,6 @@ async fn run_server() -> anyhow::Result { debug!("throttling config path: {:?}", config_path); let rpc_middleware = RpcServiceBuilder::new() - // .layer_fn(|s| - // ThrottleInterceptor::new(Some("/Users/dayong/myspace/mywork/ - // conflux-rust/crates/rpc/rpc-middlewares/src/throttling.toml"), - // "test",s)) .layer_fn(move |s| { Throttle::new(Some(config_path.to_str().unwrap()), "test", s) }) From 603f1555f558483e6f8d5c93fb3f1f805469b9d2 Mon Sep 17 00:00:00 2001 From: dayong Date: Mon, 30 Dec 2024 14:58:09 +0800 Subject: [PATCH 3/5] remove throttle test --- crates/rpc/rpc-middlewares/src/throttle.rs | 66 ---------------------- 1 file changed, 66 deletions(-) diff --git a/crates/rpc/rpc-middlewares/src/throttle.rs b/crates/rpc/rpc-middlewares/src/throttle.rs index d2ed6e1c54..2d44544f71 100644 --- a/crates/rpc/rpc-middlewares/src/throttle.rs +++ b/crates/rpc/rpc-middlewares/src/throttle.rs @@ -85,69 +85,3 @@ where S: RpcServiceT<'a> + Send + Sync + Clone + 'static } } } - -#[cfg(test)] -mod tests { - use jsonrpsee::{ - core::client::ClientT, - rpc_params, - server::{RpcServiceBuilder, Server}, - tokio, - ws_client::WsClientBuilder, - RpcModule, - }; - use std::net::SocketAddr; - - use super::*; - - async fn run_server() -> anyhow::Result { - let m = RpcServiceBuilder::new().layer_fn(|s: jsonrpsee::server::middleware::rpc::RpcService| { - Throttle::new(Some("/Users/dayong/myspace/mywork/conflux-rust/crates/rpc/rpc-middlewares/src/throttling.toml"), "test", s) - }); - - let server = Server::builder() - .set_rpc_middleware(m) - .build("127.0.0.1:0") - .await - .unwrap(); - - let addr = server.local_addr()?; - - let mut module = RpcModule::new(()); - module.register_method("say_hello", |_, _, _| "lo").unwrap(); - module - .register_method("say_goodbye", |_, _, _| "goodbye") - .unwrap(); - - let handle = server.start(module); - tokio::spawn(handle.stopped()); - - Ok(addr) - } - - async fn call_rpc(addr: SocketAddr) { - tracing_subscriber::FmtSubscriber::builder() - .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) - .try_init() - .expect("setting default subscriber failed"); - - let url = format!("ws://{}", addr); - let client = WsClientBuilder::default().build(url).await.unwrap(); - - for _ in 0..30 { - let resp_hello: String = - client.request("say_hello", rpc_params![]).await.unwrap(); - println!("resp_hello: {:?}", resp_hello); - let resp_goodbye: String = - client.request("say_goodbye", rpc_params![]).await.unwrap(); - println!("resp_goodbye: {:?}", resp_goodbye); - } - tokio::time::sleep(std::time::Duration::from_millis(100)).await; - } - - #[tokio::test] - async fn test_throttle() { - let addr = run_server().await.unwrap(); - call_rpc(addr).await; - } -} From 5e588da2e0145cf4a853082330c490bccefe1e32 Mon Sep 17 00:00:00 2001 From: dayong Date: Tue, 31 Dec 2024 11:03:42 +0800 Subject: [PATCH 4/5] resolve comments --- Cargo.lock | 4 +-- Cargo.toml | 5 ++-- crates/rpc/rpc-builder/Cargo.toml | 11 ++------ crates/rpc/rpc-middlewares/Cargo.toml | 2 +- crates/rpc/rpc-middlewares/src/metrics.rs | 17 ++++++------ crates/rpc/rpc-middlewares/src/throttle.rs | 30 ++++++++++------------ 6 files changed, 30 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 19f5005a88..5cc4807836 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1423,7 +1423,7 @@ dependencies = [ "cfxcore", "futures-util", "http 1.1.0", - "jsonrpc-core 15.1.0", + "jsonrpc-core 18.0.0", "jsonrpsee", "jsonrpsee-core", "jsonrpsee-types", @@ -1518,7 +1518,7 @@ dependencies = [ "cfx-util-macros", "futures 0.3.30", "futures-util", - "jsonrpc-core 15.1.0", + "jsonrpc-core 18.0.0", "jsonrpsee", "jsonrpsee-types", "lazy_static", diff --git a/Cargo.toml b/Cargo.toml index c752cf1558..8f011bd65e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -100,7 +100,7 @@ members = [ "crates/pos/types/move-core-types", "crates/pos/types/pow-types", "crates/pos/types/types", - "crates/pos/config/management/network-address-encryption", + "crates/pos/config/management/network-address-encryption", "crates/rpc/rpc-middlewares", ] @@ -322,7 +322,7 @@ secp256k1 = "0.30.0" clap = "2" rand = "0.7" rand_xorshift = "0.2" -rand_08 = { package = "rand", version = "0.8"} +rand_08 = { package = "rand", version = "0.8" } log = "0.4" log4rs = "1.2.0" env_logger = "0.11" @@ -351,6 +351,7 @@ docopt = "1.0" vergen = "7.0.0" target_info = "0.1" libc = "0.2" +rustls = "0.21" # conflux forked crates rocksdb = { git = "https://github.com/Conflux-Chain/rust-rocksdb.git", rev = "3773afe5b953997188f37c39308105b5deb0faac" } diff --git a/crates/rpc/rpc-builder/Cargo.toml b/crates/rpc/rpc-builder/Cargo.toml index dae0f15c17..337c0121fd 100644 --- a/crates/rpc/rpc-builder/Cargo.toml +++ b/crates/rpc/rpc-builder/Cargo.toml @@ -22,7 +22,6 @@ jsonrpc-core ={ workspace = true} jsonrpsee-core = { workspace = true } jsonrpsee-types = { workspace = true } jsonrpsee = { workspace = true, features = ["server"] } -# tower-http = { workspace = true, features = ["full"] } tower = { workspace = true, features = ["full"] } http.workspace = true pin-project = { workspace = true } @@ -38,13 +37,7 @@ cfx-util-macros = { workspace = true } log = { workspace = true } cfx-rpc-utils = { workspace = true } cfx-rpc-middlewares = { workspace = true } +futures-util = { workspace = true, features = ["io", "async-await-macro"] } [dev-dependencies] -tokio = { workspace = true, features = ["full"] } - -[dependencies.futures-util] -version = "0.3.14" -features = [ - "io", - "async-await-macro", -] \ No newline at end of file +tokio = { workspace = true, features = ["full"] } \ No newline at end of file diff --git a/crates/rpc/rpc-middlewares/Cargo.toml b/crates/rpc/rpc-middlewares/Cargo.toml index 9d37cf4d77..ef2317c7b5 100644 --- a/crates/rpc/rpc-middlewares/Cargo.toml +++ b/crates/rpc/rpc-middlewares/Cargo.toml @@ -15,7 +15,7 @@ license-file.workspace = true [dependencies] jsonrpc-core = { workspace = true } jsonrpsee = { workspace = true ,features = ["server","ws-client"]} -rustls = { version = "0.21", features = ["dangerous_configuration"] } +rustls = { workspace = true, features = ["dangerous_configuration"] } throttling = { workspace = true } futures-util = { workspace = true } log = { workspace = true } diff --git a/crates/rpc/rpc-middlewares/src/metrics.rs b/crates/rpc/rpc-middlewares/src/metrics.rs index 6728b92a66..fd048d6192 100644 --- a/crates/rpc/rpc-middlewares/src/metrics.rs +++ b/crates/rpc/rpc-middlewares/src/metrics.rs @@ -1,8 +1,10 @@ -use cfx_rpc_utils::error::jsonrpsee_error_helpers::jsonrpc_error_to_error_object_owned; use futures::{future::lazy, FutureExt}; use futures_util::future::BoxFuture; -use jsonrpc_core::Result as RpcResult; -use jsonrpsee::server::{middleware::rpc::RpcServiceT, MethodResponse}; +// use jsonrpc_core::Result as RpcResult; +use jsonrpsee::{ + core::RpcResult, + server::{middleware::rpc::RpcServiceT, MethodResponse}, +}; use jsonrpsee_types::Request; use lazy_static::lazy_static; use log::debug; @@ -67,12 +69,9 @@ where S: RpcServiceT<'a> + Send + Sync + Clone + 'static .boxed() } Err(e) => { - return Box::pin(async move { - MethodResponse::error( - req.id, - jsonrpc_error_to_error_object_owned(e), - ) - }) + return Box::pin( + async move { MethodResponse::error(req.id, e) }, + ) .boxed() } } diff --git a/crates/rpc/rpc-middlewares/src/throttle.rs b/crates/rpc/rpc-middlewares/src/throttle.rs index 2d44544f71..11804efc37 100644 --- a/crates/rpc/rpc-middlewares/src/throttle.rs +++ b/crates/rpc/rpc-middlewares/src/throttle.rs @@ -5,8 +5,10 @@ use cfx_rpc_utils::error::{ use cfx_util_macros::bail; use futures::FutureExt; use futures_util::future::BoxFuture; -use jsonrpc_core::Result as RpcResult; -use jsonrpsee::server::{middleware::rpc::RpcServiceT, MethodResponse}; +use jsonrpsee::{ + core::RpcResult, + server::{middleware::rpc::RpcServiceT, MethodResponse}, +}; use jsonrpsee_types::Request; use log::debug; use throttling::token_bucket::{ThrottleResult, TokenBucketManager}; @@ -43,16 +45,17 @@ impl Throttle { ThrottleResult::Success => Ok(()), ThrottleResult::Throttled(wait_time) => { debug!("RPC {} throttled in {:?}", name, wait_time); - bail!(request_rejected_too_many_request_error(Some(format!( - "throttled in {:?}", - wait_time - )))) + let err = request_rejected_too_many_request_error(Some( + format!("throttled in {:?}", wait_time), + )); + bail!(jsonrpc_error_to_error_object_owned(err)) } ThrottleResult::AlreadyThrottled => { debug!("RPC {} already throttled", name); - bail!(request_rejected_too_many_request_error(Some( - "already throttled, please try again later".into() - ))) + let err = request_rejected_too_many_request_error(Some( + "already throttled, please try again later".into(), + )); + bail!(jsonrpc_error_to_error_object_owned(err)) } } } @@ -74,13 +77,8 @@ where S: RpcServiceT<'a> + Send + Sync + Clone + 'static } Err(e) => { debug!("throttle interceptor: method `{}` failed", req.method); - Box::pin(async move { - MethodResponse::error( - req.id, - jsonrpc_error_to_error_object_owned(e), - ) - }) - .boxed() + Box::pin(async move { MethodResponse::error(req.id, e) }) + .boxed() } } } From 312d82e879782ab083326f32eb7c171a07125bbd Mon Sep 17 00:00:00 2001 From: dayong Date: Tue, 31 Dec 2024 16:49:34 +0800 Subject: [PATCH 5/5] fix: handle both ws and http when start async rpc server --- crates/rpc/rpc-builder/src/lib.rs | 27 +++++++++++------------ crates/rpc/rpc-middlewares/src/metrics.rs | 2 +- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/crates/rpc/rpc-builder/src/lib.rs b/crates/rpc/rpc-builder/src/lib.rs index d9d29c6b46..18bef47837 100644 --- a/crates/rpc/rpc-builder/src/lib.rs +++ b/crates/rpc/rpc-builder/src/lib.rs @@ -517,10 +517,16 @@ impl RpcServerConfig { } } + let mut result = RpcServerHandle { + http_local_addr: None, + ws_local_addr: None, + http: None, + ws: None, + }; if let Some(builder) = self.ws_server_config { let server = builder .ws_only() - .set_rpc_middleware(rpc_middleware) + .set_rpc_middleware(rpc_middleware.clone()) .build(ws_socket_addr) .await .map_err(|err| { @@ -536,12 +542,9 @@ impl RpcServerConfig { let ws_handle = ws_server.map(|ws_server| { ws_server.start(modules.ws.clone().expect("ws server error")) }); - return Ok(RpcServerHandle { - http_local_addr: None, - ws_local_addr, - http: None, - ws: ws_handle, - }); + + result.ws = ws_handle; + result.ws_local_addr = ws_local_addr; } if let Some(builder) = self.http_server_config { @@ -566,15 +569,11 @@ impl RpcServerConfig { .start(modules.http.clone().expect("http server error")) }); - return Ok(RpcServerHandle { - http_local_addr, - ws_local_addr: None, - http: http_handle, - ws: None, - }); + result.http = http_handle; + result.http_local_addr = http_local_addr; } - Err(RpcError::Custom("No valid server configured".to_string())) + Ok(result) } } diff --git a/crates/rpc/rpc-middlewares/src/metrics.rs b/crates/rpc/rpc-middlewares/src/metrics.rs index fd048d6192..03716cda48 100644 --- a/crates/rpc/rpc-middlewares/src/metrics.rs +++ b/crates/rpc/rpc-middlewares/src/metrics.rs @@ -25,7 +25,7 @@ impl Metrics { pub fn new(service: S) -> Self { Self { service } } } -impl<'a, S> Metrics { +impl Metrics { fn before(&self, name: &String) -> RpcResult<()> { // Use a global variable here because `http` and `web3` setup different // interceptors for the same RPC API.