diff --git a/.commitlintrc.yaml b/.commitlintrc.yaml new file mode 100644 index 0000000..2316995 --- /dev/null +++ b/.commitlintrc.yaml @@ -0,0 +1,7 @@ +rules: + description-empty: # Description shouldn't be empty + level: warning + subject-empty: # Subject line should exist + level: error + type-empty: # Type must not be empty + level: error \ No newline at end of file diff --git a/.samoyed/commit-msg b/.samoyed/commit-msg new file mode 100755 index 0000000..f041635 --- /dev/null +++ b/.samoyed/commit-msg @@ -0,0 +1,3 @@ +#!/usr/bin/env sh + +commitlint --edit $1 \ No newline at end of file diff --git a/.samoyed/pre-commit b/.samoyed/pre-commit index b2abe8f..5d88598 100644 --- a/.samoyed/pre-commit +++ b/.samoyed/pre-commit @@ -4,4 +4,8 @@ # exit 0 cargo fmt --check -cargo test \ No newline at end of file +cd easyhttpmock + +cargo test + +cd .. \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index fee3a16..fe04dfd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.21" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" dependencies = [ "anstyle", "anstyle-parse", @@ -34,9 +34,9 @@ checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anstyle-parse" -version = "0.2.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" dependencies = [ "utf8parse", ] @@ -67,17 +67,6 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" -[[package]] -name = "async-channel" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" -dependencies = [ - "concurrent-queue", - "event-listener 2.5.3", - "futures-core", -] - [[package]] name = "async-channel" version = "2.5.0" @@ -115,21 +104,6 @@ dependencies = [ "futures-lite", ] -[[package]] -name = "async-global-executor" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" -dependencies = [ - "async-channel 2.5.0", - "async-executor", - "async-io", - "async-lock", - "blocking", - "futures-lite", - "once_cell", -] - [[package]] name = "async-io" version = "2.6.0" @@ -150,15 +124,27 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.4.2" +version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" dependencies = [ - "event-listener 5.4.1", + "event-listener", "event-listener-strategy", "pin-project-lite", ] +[[package]] +name = "async-native-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9343dc5acf07e79ff82d0c37899f079db3534d99f189a1837c8e549c99405bec" +dependencies = [ + "futures-util", + "native-tls", + "thiserror 1.0.69", + "url", +] + [[package]] name = "async-net" version = "2.0.0" @@ -176,14 +162,14 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" dependencies = [ - "async-channel 2.5.0", + "async-channel", "async-io", "async-lock", "async-signal", "async-task", "blocking", "cfg-if", - "event-listener 5.4.1", + "event-listener", "futures-lite", "rustix", ] @@ -206,48 +192,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "async-std" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b" -dependencies = [ - "async-channel 1.9.0", - "async-global-executor", - "async-io", - "async-lock", - "async-process", - "crossbeam-utils", - "futures-channel", - "futures-core", - "futures-io", - "futures-lite", - "gloo-timers", - "kv-log-macro", - "log", - "memchr", - "once_cell", - "pin-project-lite", - "pin-utils", - "slab", - "wasm-bindgen-futures", -] - -[[package]] -name = "async-std-resolver" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0928198152da571a19145031360f34fc7569ef2dc387681565f330c811a5ba9b" -dependencies = [ - "async-std", - "async-trait", - "futures-io", - "futures-util", - "pin-utils", - "socket2 0.5.10", - "trust-dns-resolver", -] - [[package]] name = "async-task" version = "4.7.1" @@ -279,9 +223,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" -version = "1.16.1" +version = "1.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94bffc006df10ac2a68c83692d734a465f8ee6c5b384d8545a636f81d858f4bf" +checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc" dependencies = [ "aws-lc-sys", "zeroize", @@ -289,9 +233,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.38.0" +version = "0.39.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4321e568ed89bb5a7d291a7f37997c2c0df89809d7b6d12062c81ddb54aa782e" +checksum = "83a25cf98105baa966497416dbd42565ce3a8cf8dbfd59803ec9ad46f3126399" dependencies = [ "cc", "cmake", @@ -323,7 +267,7 @@ version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" dependencies = [ - "async-channel 2.5.0", + "async-channel", "async-task", "futures-io", "futures-lite", @@ -344,9 +288,9 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cc" -version = "1.2.57" +version = "1.2.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" +checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283" dependencies = [ "find-msvc-tools", "jobserver", @@ -354,12 +298,6 @@ dependencies = [ "shlex", ] -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - [[package]] name = "cfg-if" version = "1.0.4" @@ -385,9 +323,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.0" +version = "4.5.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" +checksum = "52fa72306bb30daf11bc97773431628e5b4916e97aaa74b7d3f625d4d495da02" dependencies = [ "clap_builder", "clap_derive", @@ -395,9 +333,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.6.0" +version = "4.5.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +checksum = "2071365c5c56eae7d77414029dde2f4f4ba151cf68d5a3261c9a40de428ace93" dependencies = [ "anstyle", "clap_lex", @@ -405,9 +343,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.6.0" +version = "4.5.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" +checksum = "dec5be1eea072311774b7b84ded287adbd9f293f9d23456817605c6042f4f5e0" dependencies = [ "heck", "proc-macro2", @@ -417,15 +355,15 @@ dependencies = [ [[package]] name = "clap_lex" -version = "1.1.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" +checksum = "0e78417baa3b3114dc0e95e7357389a249c4da97c3c2b540700079db6171bfd7" [[package]] name = "cmake" -version = "0.1.57" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" dependencies = [ "cc", ] @@ -436,16 +374,6 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" -[[package]] -name = "combine" -version = "4.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" -dependencies = [ - "bytes", - "memchr", -] - [[package]] name = "concurrent-queue" version = "2.5.0" @@ -471,6 +399,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation" version = "0.10.1" @@ -497,23 +435,29 @@ dependencies = [ ] [[package]] -name = "crossbeam-utils" -version = "0.8.21" +name = "crossbeam-channel" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] [[package]] -name = "crossfire" -version = "3.1.6" +name = "crossbeam-epoch" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c93b03ce6b0c57c0c37a1a347fc51e4a7d4eca4ea97e06a456093df88376e8" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ "crossbeam-utils", - "futures-core", - "parking_lot", - "smallvec", ] +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "data-encoding" version = "2.10.0" @@ -522,19 +466,14 @@ checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "deboa" -version = "0.1.0-beta.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6350e6d582813b061aff007ffeca894a421f4ccd58c03d289062a7a4f86f868" +version = "0.1.0-beta.16" dependencies = [ - "async-executor", - "async-std-resolver", "async-trait", "base64", "bytes", "cfg-if", "cookie", "futures", - "futures-rustls", "h3", "h3-quinn", "http", @@ -545,20 +484,45 @@ dependencies = [ "hyper-util", "indexmap", "log", - "macro_rules_attribute", + "minimime", + "mockall", + "rand 0.9.2", + "regex", + "serde", + "thiserror 2.0.18", + "time 0.2.27", + "url", + "urlencoding", +] + +[[package]] +name = "deboa-tokio" +version = "0.1.0-beta.1" +dependencies = [ + "async-trait", + "base64", + "bytes", + "cookie", + "deboa", + "futures", + "futures-rustls", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-body-utils", + "hyper-util", + "indexmap", + "log", "minimime", "mockall", "quinn", "rand 0.9.2", "regex", - "rt-gate", "rustls", "rustls-native-certs", "rustls-pki-types", "serde", - "smol", - "smol-hyper", - "smol-macros", "thiserror 2.0.18", "time 0.2.27", "tokio", @@ -572,9 +536,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.8" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", ] @@ -610,17 +574,44 @@ checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "easyhttpmock" -version = "0.1.3-beta.1" +version = "0.1.3-beta.7" dependencies = [ "bytes", + "googletest", "http", "http-body-util", "hyper", "hyper-util", "rand 0.10.0", "thiserror 2.0.18", +] + +[[package]] +name = "easyhttpmock-vetis-smol" +version = "0.1.0-beta.1" +dependencies = [ + "easyhttpmock", + "googletest", + "http", + "macro_rules_attribute", + "rand 0.9.2", + "smol", + "smol-macros", + "vetis-smol", +] + +[[package]] +name = "easyhttpmock-vetis-tokio" +version = "0.1.0-beta.1" +dependencies = [ + "deboa", + "deboa-tokio", + "easyhttpmock", + "googletest", + "http", + "rand 0.9.2", "tokio", - "vetis", + "vetis-tokio", ] [[package]] @@ -643,9 +634,9 @@ dependencies = [ [[package]] name = "env_filter" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a1c3cc8e57274ec99de65301228b537f1e4eedc1b8e0f9411c6caac8ae7308f" +checksum = "32e90c2accc4b07a8456ea0debdc2e7587bdd890680d71173a15d4ae604f6eef" dependencies = [ "log", "regex", @@ -653,9 +644,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.9" +version = "0.11.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2daee4ea451f429a58296525ddf28b45a3b64f1acf6587e2067437bb11e218d" +checksum = "0621c04f2196ac3f488dd583365b9c09be011a4ab8b9f37248ffcc8f6198b56a" dependencies = [ "anstream", "anstyle", @@ -680,12 +671,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - [[package]] name = "event-listener" version = "5.4.1" @@ -703,27 +688,15 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ - "event-listener 5.4.1", + "event-listener", "pin-project-lite", ] -[[package]] -name = "fastbloom" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7f34442dbe69c60fe8eaf58a8cafff81a1f278816d8ab4db255b3bef4ac3c4" -dependencies = [ - "getrandom 0.3.4", - "libm", - "rand 0.9.2", - "siphasher", -] - [[package]] name = "fastrand" -version = "2.3.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "find-msvc-tools" @@ -743,6 +716,21 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -754,9 +742,12 @@ dependencies = [ [[package]] name = "fragile" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dd6caf6059519a65843af8fe2a3ae298b14b80179855aeb4adc2c1934ee619" +checksum = "8878864ba14bb86e818a412bfd6f18f9eabd4ec0f008a28e8f7eb61db532fcf9" +dependencies = [ + "futures-core", +] [[package]] name = "fs_extra" @@ -898,35 +889,46 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "r-efi", + "r-efi 5.3.0", "wasip2", "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", "libc", - "r-efi", + "r-efi 6.0.0", "rand_core 0.10.0", "wasip2", "wasip3", ] [[package]] -name = "gloo-timers" -version = "0.3.0" +name = "googletest" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +checksum = "06597b7d02ee58b9a37f522785ac15b9e18c6b178747c4439a6c03fbb35ea753" dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", + "googletest_macro", + "num-traits", + "regex", + "rustversion", +] + +[[package]] +name = "googletest_macro" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31d9f07c9c19b855faebf71637be3b43f8e13a518aece5d61a3beee7710b4ef" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", ] [[package]] @@ -1003,21 +1005,6 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" -[[package]] -name = "hotpath" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fde50be006a0fe95cc2fd6d25d884aa6932218e4055d7df2fa0d95c386acf8d" -dependencies = [ - "hotpath-macros", -] - -[[package]] -name = "hotpath-macros" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd884cee056e269e41e1127549458e1c4e309f31897ebbc1416982a74d40a5b5" - [[package]] name = "http" version = "1.4.0" @@ -1065,9 +1052,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" dependencies = [ "atomic-waker", "bytes", @@ -1080,7 +1067,6 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "pin-utils", "smallvec", "tokio", "want", @@ -1088,15 +1074,12 @@ dependencies = [ [[package]] name = "hyper-body-utils" -version = "0.1.6-beta.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4eff608807e6c410941de3e22c17044fecec132e20dc21f33247cc31509f14f" +version = "0.1.6-beta.7" dependencies = [ "bytes", "futures", "h3", "h3-quinn", - "hotpath", "http-body-util", "hyper", "macro_rules_attribute", @@ -1120,7 +1103,7 @@ dependencies = [ "hyper", "libc", "pin-project-lite", - "socket2 0.6.2", + "socket2", "tokio", "tower-service", "tracing", @@ -1128,22 +1111,21 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.1.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" dependencies = [ "displaydoc", - "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locale_core" -version = "2.1.1" +name = "icu_locid" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" dependencies = [ "displaydoc", "litemap", @@ -1152,61 +1134,99 @@ dependencies = [ "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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" + [[package]] name = "icu_normalizer" -version = "2.1.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" dependencies = [ + "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", "icu_provider", "smallvec", + "utf16_iter", + "utf8_iter", + "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "2.1.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" [[package]] name = "icu_properties" -version = "2.1.2" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" dependencies = [ + "displaydoc", "icu_collections", - "icu_locale_core", + "icu_locid_transform", "icu_properties_data", "icu_provider", - "zerotrie", + "tinystr", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.1.2" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" [[package]] name = "icu_provider" -version = "2.1.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" dependencies = [ "displaydoc", - "icu_locale_core", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", "writeable", "yoke", "zerofrom", - "zerotrie", "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", + "quote", + "syn 2.0.117", +] + [[package]] name = "id-arena" version = "2.3.0" @@ -1236,9 +1256,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" dependencies = [ "icu_normalizer", "icu_properties", @@ -1246,9 +1266,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.13.0" +version = "2.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" dependencies = [ "equivalent", "hashbrown 0.16.1", @@ -1256,18 +1276,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "ipconfig" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" -dependencies = [ - "socket2 0.5.10", - "widestring", - "windows-sys 0.48.0", - "winreg", -] - [[package]] name = "ipnet" version = "2.12.0" @@ -1282,9 +1290,9 @@ checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itoa" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jiff" @@ -1310,28 +1318,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "jni" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys", - "log", - "thiserror 1.0.69", - "walkdir", - "windows-sys 0.45.0", -] - -[[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.34" @@ -1344,23 +1330,14 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.85" +version = "0.3.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9" dependencies = [ "once_cell", "wasm-bindgen", ] -[[package]] -name = "kv-log-macro" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" -dependencies = [ - "log", -] - [[package]] name = "leb128fmt" version = "0.1.0" @@ -1369,15 +1346,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.183" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" - -[[package]] -name = "libm" -version = "0.2.16" +version = "0.2.184" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" +checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" [[package]] name = "libmimalloc-sys" @@ -1403,9 +1374,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.8.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" [[package]] name = "lock_api" @@ -1421,9 +1392,6 @@ name = "log" version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" -dependencies = [ - "value-bag", -] [[package]] name = "lru-cache" @@ -1479,9 +1447,9 @@ checksum = "9bcaa89828ea1e6ab547d9d61ae49f1f9336459b593f285ecc402af9152e16b2" [[package]] name = "mio" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", "wasi", @@ -1514,6 +1482,43 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "moka" +version = "0.12.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957228ad12042ee839f93c8f257b62b4c0ab5eaae1d4fa60de53b27c9d7c5046" +dependencies = [ + "async-lock", + "crossbeam-channel", + "crossbeam-epoch", + "crossbeam-utils", + "equivalent", + "event-listener", + "futures-util", + "parking_lot", + "portable-atomic", + "smallvec", + "tagptr", + "uuid", +] + +[[package]] +name = "native-tls" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe 0.1.6", + "openssl-sys", + "schannel", + "security-framework 2.11.1", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nibble_vec" version = "0.1.0" @@ -1525,9 +1530,18 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.0" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] [[package]] name = "once_cell" @@ -1541,12 +1555,56 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "openssl" +version = "0.10.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfe4646e360ec77dff7dde40ed3d6c5fee52d156ef4a62f53973d38294dad87f" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + [[package]] name = "openssl-probe" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" +[[package]] +name = "openssl-sys" +version = "0.9.113" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad2f2c0eba47118757e4c6d2bff2838f3e0523380021356e7875e858372ce644" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "parking" version = "2.2.1" @@ -1606,12 +1664,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - [[package]] name = "piper" version = "0.2.5" @@ -1623,6 +1675,12 @@ dependencies = [ "futures-io", ] +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + [[package]] name = "polling" version = "3.11.0" @@ -1652,15 +1710,6 @@ dependencies = [ "portable-atomic", ] -[[package]] -name = "potential_utf" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" -dependencies = [ - "zerovec", -] - [[package]] name = "powerfmt" version = "0.2.0" @@ -1743,7 +1792,7 @@ dependencies = [ "rustc-hash", "rustls", "smol", - "socket2 0.6.2", + "socket2", "thiserror 2.0.18", "tokio", "tracing", @@ -1758,7 +1807,6 @@ checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" dependencies = [ "aws-lc-rs", "bytes", - "fastbloom", "getrandom 0.3.4", "lru-slab", "rand 0.9.2", @@ -1766,7 +1814,6 @@ dependencies = [ "rustc-hash", "rustls", "rustls-pki-types", - "rustls-platform-verifier", "slab", "thiserror 2.0.18", "tinyvec", @@ -1783,7 +1830,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.2", + "socket2", "tracing", "windows-sys 0.60.2", ] @@ -1803,6 +1850,12 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "radix_trie" version = "0.3.0" @@ -1841,7 +1894,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" dependencies = [ "chacha20", - "getrandom 0.4.1", + "getrandom 0.4.2", "rand_core 0.10.0", ] @@ -1927,12 +1980,6 @@ version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" -[[package]] -name = "resolv-conf" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" - [[package]] name = "ring" version = "0.17.14" @@ -1947,16 +1994,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rt-gate" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d78077a3e29aa7f843d854b0133957d485ecaff3b47c657c156a2808ced16dc" -dependencies = [ - "smol", - "tokio", -] - [[package]] name = "rustc-hash" version = "2.1.1" @@ -1992,9 +2029,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" dependencies = [ "aws-lc-rs", - "log", "once_cell", - "ring", "rustls-pki-types", "rustls-webpki", "subtle", @@ -2007,10 +2042,10 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" dependencies = [ - "openssl-probe", + "openssl-probe 0.2.1", "rustls-pki-types", "schannel", - "security-framework", + "security-framework 3.6.0", ] [[package]] @@ -2023,38 +2058,11 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustls-platform-verifier" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" -dependencies = [ - "core-foundation", - "core-foundation-sys", - "jni", - "log", - "once_cell", - "rustls", - "rustls-native-certs", - "rustls-platform-verifier-android", - "rustls-webpki", - "security-framework", - "security-framework-sys", - "webpki-root-certs", - "windows-sys 0.61.2", -] - -[[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.103.9" +version = "0.103.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" dependencies = [ "aws-lc-rs", "ring", @@ -2074,15 +2082,6 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - [[package]] name = "schannel" version = "0.1.29" @@ -2096,16 +2095,29 @@ dependencies = [ name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] [[package]] name = "security-framework" -version = "3.7.0" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +checksum = "d17b898a6d6948c3a8ee4372c17cb384f90d2e6e912ef00895b14fd7ab54ec38" dependencies = [ "bitflags", - "core-foundation", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -2132,9 +2144,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "semver-parser" @@ -2221,9 +2233,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b57709da74f9ff9f4a27dce9526eec25ca8407c45a7887243b031a58935fb8e" +checksum = "b2a0c28ca5908dbdbcd52e6fdaa00358ab88637f8ab33e1f188dd510eb44b53d" dependencies = [ "libc", "signal-hook-registry", @@ -2239,12 +2251,6 @@ dependencies = [ "libc", ] -[[package]] -name = "siphasher" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" - [[package]] name = "slab" version = "0.4.12" @@ -2263,7 +2269,7 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a33bd3e260892199c3ccfc487c88b2da2265080acb316cd920da72fdfd7c599f" dependencies = [ - "async-channel 2.5.0", + "async-channel", "async-executor", "async-fs", "async-io", @@ -2280,8 +2286,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7428a49d323867702cd12b97b08a6b0104f39ec13b49117911f101271321bc1a" dependencies = [ - "async-executor", - "async-io", "futures-io", "hyper", "pin-project-lite", @@ -2296,28 +2300,18 @@ dependencies = [ "async-executor", "async-io", "async-lock", - "event-listener 5.4.1", + "event-listener", "futures-lite", ] [[package]] name = "socket2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "socket2" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -2423,6 +2417,25 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "tagptr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.3.4", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + [[package]] name = "termtree" version = "0.5.1" @@ -2486,24 +2499,24 @@ dependencies = [ [[package]] name = "time" -version = "0.3.47" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "itoa", "num-conv", "powerfmt", - "serde_core", + "serde", "time-core", - "time-macros 0.2.27", + "time-macros 0.2.22", ] [[package]] name = "time-core" -version = "0.1.8" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" @@ -2517,9 +2530,9 @@ dependencies = [ [[package]] name = "time-macros" -version = "0.2.27" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", @@ -2540,9 +2553,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.2" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ "displaydoc", "zerovec", @@ -2565,26 +2578,25 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.50.0" +version = "1.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +checksum = "f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c" dependencies = [ "bytes", "libc", "mio", - "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.2", + "socket2", "tokio-macros", "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.6.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", @@ -2626,7 +2638,6 @@ version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ - "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -2685,12 +2696,10 @@ checksum = "10a3e6c3aff1718b3c73e395d1f35202ba2ffa847c6a62eea0db8fb4cfe30be6" dependencies = [ "cfg-if", "futures-util", - "ipconfig", "lru-cache", "once_cell", "parking_lot", "rand 0.8.5", - "resolv-conf", "smallvec", "thiserror 1.0.69", "tokio", @@ -2761,6 +2770,12 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" +[[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" @@ -2774,10 +2789,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] -name = "value-bag" -version = "1.12.0" +name = "uuid" +version = "1.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f" +dependencies = [ + "getrandom 0.3.4", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" @@ -2788,15 +2814,37 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vetis" version = "0.1.4-beta.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50124f6d436fd2047a885a7d75811c4eec05e1d9429d00137a68ecddc049cd0d" dependencies = [ - "async-signal", - "blocking", + "async-lock", "bytes", "cfg-if", "clap", - "crossfire", + "env_logger", + "futures-util", + "http", + "http-body-util", + "hyper-body-utils", + "log", + "mimalloc", + "radix_trie", + "rand 0.9.2", + "serde", + "serde_yaml_ng", + "socket2", + "thiserror 2.0.18", + "time 0.3.41", + "url", +] + +[[package]] +name = "vetis-smol" +version = "0.1.0-beta.1" +dependencies = [ + "async-lock", + "async-native-tls", + "async-signal", + "bytes", + "clap", "deboa", "env_logger", "futures-lite", @@ -2807,6 +2855,7 @@ dependencies = [ "http", "http-body-util", "hyper", + "hyper-body-utils", "hyper-util", "log", "macro_rules_attribute", @@ -2815,7 +2864,6 @@ dependencies = [ "quinn", "radix_trie", "rand 0.9.2", - "rt-gate", "rustls", "serde", "serde_yaml_ng", @@ -2823,22 +2871,51 @@ dependencies = [ "smol", "smol-hyper", "smol-macros", + "socket2", "thiserror 2.0.18", - "time 0.3.47", - "tokio", - "tokio-rustls", - "tokio-util", + "time 0.3.41", "url", + "vetis", ] [[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +name = "vetis-tokio" +version = "0.1.0-beta.1" dependencies = [ - "same-file", - "winapi-util", + "async-lock", + "bytes", + "clap", + "deboa", + "env_logger", + "futures-rustls", + "futures-util", + "h3", + "h3-quinn", + "http", + "http-body-util", + "hyper", + "hyper-body-utils", + "hyper-util", + "log", + "mimalloc", + "minimime", + "moka", + "peekable", + "quinn", + "radix_trie", + "rand 0.9.2", + "regex", + "rustls", + "serde", + "serde_yaml_ng", + "socket2", + "thiserror 2.0.18", + "time 0.3.41", + "tokio", + "tokio-rustls", + "tokio-util", + "url", + "vetis", ] [[package]] @@ -2876,9 +2953,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.108" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0" dependencies = [ "cfg-if", "once_cell", @@ -2887,25 +2964,11 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" -dependencies = [ - "cfg-if", - "futures-util", - "js-sys", - "once_cell", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "wasm-bindgen-macro" -version = "0.2.108" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2913,9 +2976,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.108" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2" dependencies = [ "bumpalo", "proc-macro2", @@ -2926,9 +2989,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.108" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b" dependencies = [ "unicode-ident", ] @@ -2964,17 +3027,7 @@ dependencies = [ "bitflags", "hashbrown 0.15.5", "indexmap", - "semver 1.0.27", -] - -[[package]] -name = "web-sys" -version = "0.3.85" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" -dependencies = [ - "js-sys", - "wasm-bindgen", + "semver 1.0.28", ] [[package]] @@ -2987,30 +3040,15 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki-root-certs" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "webpki-roots" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" dependencies = [ "rustls-pki-types", ] -[[package]] -name = "widestring" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" - [[package]] name = "winapi" version = "0.3.9" @@ -3027,15 +3065,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys 0.61.2", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -3048,24 +3077,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -3093,36 +3104,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -3156,18 +3137,6 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -3180,18 +3149,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -3204,18 +3161,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -3240,18 +3185,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -3264,18 +3197,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -3288,18 +3209,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -3312,18 +3221,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -3336,16 +3233,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "wit-bindgen" version = "0.51.0" @@ -3426,7 +3313,7 @@ dependencies = [ "id-arena", "indexmap", "log", - "semver 1.0.27", + "semver 1.0.28", "serde", "serde_derive", "serde_json", @@ -3434,18 +3321,25 @@ dependencies = [ "wasmparser", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + [[package]] name = "writeable" -version = "0.6.2" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "yoke" -version = "0.8.1" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ + "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -3453,9 +3347,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.1" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", @@ -3465,18 +3359,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.42" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2578b716f8a7a858b7f02d5bd870c14bf4ddbbcf3a4c05414ba6503640505e3" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.42" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" dependencies = [ "proc-macro2", "quote", @@ -3485,18 +3379,18 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", @@ -3510,22 +3404,11 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" -[[package]] -name = "zerotrie" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - [[package]] name = "zerovec" -version = "0.11.5" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" dependencies = [ "yoke", "zerofrom", @@ -3534,9 +3417,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index e4183fc..5681183 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,37 +1,21 @@ -[package] -name = "easyhttpmock" -version = "0.1.3-beta.1" -edition = "2021" -authors = ["Rogerio Araújo "] -repository = "https://github.com/ararog/easyhttpmock" -homepage = "https://github.com/ararog/easyhttpmock" -description = "EasyHttpMock is a simple HTTP mock server for testing HTTP clients." -readme = "README.md" -license = "MIT" -keywords = ["http", "mock", "testing"] -publish = true -rust-version = "1.75.0" +[workspace] +# Use the latest resolver available +resolver = "3" +# Include both parts of the library in the workspace +members = [ + "easyhttpmock", + "easyhttpmock-vetis-smol", + "easyhttpmock-vetis-tokio", +] -[features] -default = ["tokio-rt", "http2", "tokio-rust-tls"] +[profile.release] +strip = true +opt-level = "z" +lto = true +codegen-units = 1 +panic = "abort" -tokio-rt = ["vetis/tokio-rt", "vetis/__deboa_tokio"] -smol-rt = ["vetis/smol-rt", "vetis/__deboa_smol"] - -tokio-rust-tls = ["vetis/tokio-rust-tls"] -smol-rust-tls = ["vetis/smol-rust-tls"] - -http1 = ["vetis/http1"] -http2 = ["vetis/http2"] -http3 = ["vetis/http3"] - -[dependencies] -bytes = "1.11.0" -http = "1.4.0" -http-body-util = "0.1.3" -hyper = { version = "1.8.1", features = ["full"] } -hyper-util = "0.1.9" -rand = "0.10.0" -thiserror = "2.0.17" -tokio = { version = "1.50", features = ["full"] } -vetis = { version = "0.1.4-beta.5", optional = true, default-features = false } +# The profile that 'dist' will build with +[profile.dist] +inherits = "release" +lto = "thin" diff --git a/LICENSE-APACHE.md b/LICENSE-APACHE.md new file mode 100644 index 0000000..c6c6f01 --- /dev/null +++ b/LICENSE-APACHE.md @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2020 quininer kel + +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. \ No newline at end of file diff --git a/LICENSE b/LICENSE-MIT.md similarity index 93% rename from LICENSE rename to LICENSE-MIT.md index f8e760d..01c3389 100644 --- a/LICENSE +++ b/LICENSE-MIT.md @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2026 ararog +Copyright (c) 2025 Rogerio Pereira Araújo Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -19,3 +19,4 @@ 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. + diff --git a/README.md b/README.md index 2a1c91b..5e2f938 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # EasyHttpMock +[![Crates.io downloads](https://img.shields.io/crates/d/easyhttpmock)](https://crates.io/crates/easyhttpmock) [![crates.io](https://img.shields.io/crates/v/easyhttpmock?style=flat-square)](https://crates.io/crates/easyhttpmock) [![Build Status](https://github.com/ararog/easyhttpmock/actions/workflows/rust.yml/badge.svg?event=push)](https://github.com/ararog/easyhttpmock/actions/workflows/rust.yml) ![Crates.io MSRV](https://img.shields.io/crates/msrv/easyhttpmock) [![Documentation](https://docs.rs/easyhttpmock/badge.svg)](https://docs.rs/easyhttpmock/latest/easyhttpmock) [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/ararog/easyhttpmock/blob/main/LICENSE.md) [![codecov](https://codecov.io/gh/ararog/easyhttpmock/graph/badge.svg?token=T0HSBAPVSI)](https://codecov.io/gh/ararog/easyhttpmock) + + **The effortless HTTP mock server for seamless API testing** **EasyHttpMock** is a powerful yet simple HTTP mock server designed specifically for testing HTTP clients. Built on top of [VeTiS](https://github.com/ararog/vetis), it provides a clean, intuitive API for creating realistic mock endpoints that simulate real-world API behavior, making your testing workflow faster and more reliable. @@ -26,25 +29,25 @@ easyhttpmock = { version = "0.1.1", features = ["tokio-rt", "http2", "tokio-rust Here's how simple it is to create a mock HTTP server for testing: ```rust -use bytes::Bytes; use http::StatusCode; -use http_body_util::Full; -use hyper::Response; use easyhttpmock::{ - config::EasyHttpMockConfig, - server::{adapters::vetis_adapter::VetisServerAdapter, PortGenerator}, EasyHttpMock, -}; - -use deboa::{cert::ContentEncoding, request::DeboaRequest, Client}; - -use vetis::{ + config::EasyHttpMockConfig, server::{ - config::{SecurityConfig, ServerConfig}, + PortGenerator, + adapters::vetis_adapter::{VetisAdapter, VetisAdapterConfig}, }, }; +use deboa::{ + Client, + cert::{Certificate, ContentEncoding}, + request::DeboaRequest, +}; + +use vetis::Response; + pub const CA_CERT: &[u8] = include_bytes!("../certs/ca.der"); pub const CA_CERT_PEM: &[u8] = include_bytes!("../certs/ca.crt"); @@ -53,25 +56,25 @@ pub const SERVER_KEY: &[u8] = include_bytes!("../certs/server.key.der"); #[tokio::main] async fn main() -> Result<(), Box> { - let tls_config = SecurityConfig::builder() - .cert(SERVER_CERT.to_vec()) - .key(SERVER_KEY.to_vec()) - .build(); - - let vetis_config = ServerConfig::builder() - .security(tls_config) + let vetis_adapter_config = VetisAdapterConfig::builder() + .interface("0.0.0.0") .with_random_port() + .cert(Some(SERVER_CERT.to_vec())) + .key(Some(SERVER_KEY.to_vec())) + .ca(Some(CA_CERT.to_vec())) .build(); - let config = EasyHttpMockConfig::::builder() - .server_config(vetis_config) + let config = EasyHttpMockConfig::::builder() + .server_config(vetis_adapter_config) .build(); - let mut server = EasyHttpMock::new(config); + let mut server = EasyHttpMock::new(config)?; #[allow(unused_must_use)] let result = server .start(|_| async move { - Ok(Response::new(Full::new(Bytes::from("Hello World")))) + Ok(Response::builder() + .status(StatusCode::OK) + .text("Hello World")) }) .await; @@ -80,10 +83,11 @@ async fn main() -> Result<(), Box> { }); let client = Client::builder() - .certificate(deboa::cert::Certificate::from_slice(CA_CERT, ContentEncoding::DER)) + .certificate(Certificate::from_slice(CA_CERT, ContentEncoding::DER)) .build(); - let request = DeboaRequest::get(server.url("/anything"))?.build()?; + let url = server.url("/anything"); + let request = DeboaRequest::get(url)?.build()?; let response = client .execute(request) @@ -96,7 +100,7 @@ async fn main() -> Result<(), Box> { server .stop() .await?; - + Ok(()) } ``` diff --git a/certs/README.md b/certs/README.md new file mode 100644 index 0000000..c9c3b43 --- /dev/null +++ b/certs/README.md @@ -0,0 +1,92 @@ +# Generating self-signed certificates + +## CA Certificate + +```bash +openssl req -x509 -noenc -subj '/CN=My CA' -newkey rsa:4096 -keyout ca.key -out ca.crt -days 3650 +``` + +### Export as DER: + +```bash +openssl x509 -in ca.crt -out ca.der -outform DER +``` + +## Server + +### Private Key + +```bash +openssl genrsa -out server.key 4096 +``` + +### Certificate Request + +```bash +openssl req -new -key server.key -out server.csr -subj '/CN=localhost' +``` + +### Export as DER: + +```bash +openssl x509 -inform pem -in server.key -outform der -out server.der.key +``` + +### Create server.cnf + +```bash +# server.cnf +[ req ] +prompt = no +distinguished_name = req_distinguished_name +req_extensions = req_ext + +[ req_distinguished_name ] +CN = localhost + +[ req_ext ] +subjectAltName = @alt_names + +[ alt_names ] +DNS.1 = localhost +IP.1 = 127.0.0.1 +# Add more DNS names or IPs as needed, e.g., DNS.2 = myapp.internal +``` + +### Sign the Certificate Request + +```bash +openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 3650 -extfile server.cnf -extensions req_ext +``` + +### Export as DER: + +```bash +openssl x509 -in server.crt -outform der -out server.der +``` + +## Client + +### Private Key and certificate request + +```bash +openssl req -new -newkey rsa:4096 -nodes -keyout client.key -out client.csr -config client.cnf -sha256 +``` + +### Sign the Certificate Request + +```bash +openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 3650 -sha256 -extfile client.cnf +``` + +### Export as PKCS#8 DER + +```bash +openssl pkcs8 -topk8 -inform PEM -outform DER -in client.key -out client.der.key -nocrypt +``` + +### Export as PKCS#12 (client.key and client.crt are PEM encoded) + +```bash +openssl pkcs12 -export -out client.p12 -inkey client.key -in client.crt -name "Client Certificate" +``` diff --git a/certs/ca.crt b/certs/ca.crt new file mode 100644 index 0000000..0d855a2 --- /dev/null +++ b/certs/ca.crt @@ -0,0 +1,29 @@ +-----BEGIN CERTIFICATE----- +MIIFATCCAumgAwIBAgIUGP1tov72wLh6qoyNxUKGX2VPeZEwDQYJKoZIhvcNAQEL +BQAwEDEOMAwGA1UEAwwFTXkgQ0EwHhcNMjYwMTE0MDAxNDMyWhcNMzYwMTEyMDAx +NDMyWjAQMQ4wDAYDVQQDDAVNeSBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC +AgoCggIBAOQALuFzLrzssBoyvILUX9IYzIGIPbDza5pY5XQ9OyuOQonuDALz+0Zp +ta72zB13RG5CNmol6Nfkjh/wAY4leJuM9lgreLs7j31Il+rt4MwHcfHDfW+Wti88 +a18BjBKHAq1b3Dt+PHXj5do/BAOX/3xv6U4uZ7JHpvMdkuGbYXbZkQvyLmDp+vF6 +ZP0n/kM2owRtJWYwDaNi2qHn+TDRteA1Q3m5bzcbF6iJtvRsXXEKqDU1Ei6mlDtp +eKK7OvAKktcwNM3f78DIyVRFfeZLRbiqnRM+C9qH/YNx4/DLX3iUEQL1oYPKD/Kg +MumNXaizRImCUX2JYKzWUMSS1g0j1RknMLJMW+i49bWKVG8hm6k13DOvGuHRjzRs +MxNL+B5QKNd2bS20GHCsAWV8+EMwEFPKORX4RGn4bMML34MpMC683d3EFXdI9UP5 +6p+0uvw/LvSiXHmqfwqhPelgpkKz+0IVF+31TE+n37OimO+dq4boNRP4BXjo+AQs +Pcy/dmLlfJ9ghAXSpJGzXHAT9JeQzWlOZDMOFXKIygs9HHrARxJjaz1kjlGdXJ32 +h6Fwn+FkCKEeEaYu7FPVc+u/EZfaedgnPcg/06YlWbRgdxYeT7bEizF31+FW7D02 +Cc0yMvfUzzWdtli3mAWHq4vQwx0h2tdSfafhSUtGcvU5F0zuPLhvAgMBAAGjUzBR +MB0GA1UdDgQWBBSgG1KEEwWG42vTne9aa2N+N7w2TTAfBgNVHSMEGDAWgBSgG1KE +EwWG42vTne9aa2N+N7w2TTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA +A4ICAQBrZT+nVQwVKHa0XU8f9bikHtNWA1qHglwssqu85gW6qIvQBCuCZ6553g6b +bD8Dazv4Bjs73f/FV+GutL7AStpwtpz2MJE1JqkKZ3uiLCEo7XqHNBNfMyLGVxWH +GBgVhsxo9aH3gl69odgHVyYe+fFKPuyd1flhzwkT3c/Pq4l+j/fYie7AwXMFUlJY +Bl/FNaqPtjrKwkhGCqLDNmraM97q2i7Wy0sw1TWgtbIhtT0vWeSjGS+LFbl2pgzr +bIkLjD5j3SQBEh2JEmXli5jJw5EG3WBS3ogCBVTacSiIihnNyl1f9X6TiR4N8R9Q +Cp9Wg45niLlgzIi0K8kQO0FDYnl+iWRf8HtxPO+vD/uqfVpTi2aY6juWLjJm06MF +sIEsmuiO/oCO0baynKp/iIko317xHu1OZ8r6kX8pOY8uSHD473Tgd3rJe9kBPfBS +Fh8zy3dyrfxnC59DPxIexgLG3SPeRfseZUNUrBlu4o6OIApqByBBD0FJKNWKERt1 ++dtGbPBL3HC76Gdvo2jU3xu65Fp7hQCvcMi9YUX5j+2tV6mQLZyvodjJjwRtcoT1 +jZxspFtfWf6cgK+tLjl4QFHzr4w/uqyViMOLyUvaBimoyz+NMlJWbmMxfq7BVegu +8ioBqZTRQo12GQRNZ+ZGJP1aF4e9MsY97e33L7OqeL/xarK8YA== +-----END CERTIFICATE----- diff --git a/certs/ca.der b/certs/ca.der new file mode 100644 index 0000000..25fc996 Binary files /dev/null and b/certs/ca.der differ diff --git a/certs/ca.key b/certs/ca.key new file mode 100644 index 0000000..b2e89e8 --- /dev/null +++ b/certs/ca.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +RQ9uH+yytFlyLKF0w73lgX1mu6Lomw== +-----END PRIVATE KEY----- diff --git a/certs/client.cnf b/certs/client.cnf new file mode 100644 index 0000000..8087e72 --- /dev/null +++ b/certs/client.cnf @@ -0,0 +1,20 @@ +[ req ] +prompt = no +distinguished_name = req_distinguished_name +req_extensions = v3_req + +[ req_distinguished_name ] +# Provide your client details here +C = US +ST = VA +L = SomeCity +O = MyCompany +OU = MyDivision +CN = testuser + +[ v3_req ] +basicConstraints = CA:FALSE +keyUsage = critical, digitalSignature, keyEncipherment +extendedKeyUsage = critical, clientAuth +# Optional: Add Subject Alternative Name (SAN) if required +# subjectAltName = DNS:testuser.example.com, IP:192.168.1.100 diff --git a/certs/client.crt b/certs/client.crt new file mode 100644 index 0000000..5c92590 --- /dev/null +++ b/certs/client.crt @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFfDCCA2SgAwIBAgIUXfIvupWS+2Yhgp58m+BBOwrl1K8wDQYJKoZIhvcNAQEL +BQAwEDEOMAwGA1UEAwwFTXkgQ0EwHhcNMjYwMTE0MDE0NjEwWhcNMzYwMTEyMDE0 +NjEwWjBpMQswCQYDVQQGEwJVUzELMAkGA1UECAwCVkExETAPBgNVBAcMCFNvbWVD +aXR5MRIwEAYDVQQKDAlNeUNvbXBhbnkxEzARBgNVBAsMCk15RGl2aXNpb24xETAP +BgNVBAMMCHRlc3R1c2VyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA +soF9CclKGALHr+I/4kbC+3UoGc9sX7s7GLb8BqmQ+9PdWzx/MheMuNuE4bvManLS +xXPiHyzWpU3qE95rD8IMamzPt+nIBaS/PbgkasQoocH2cZofVBgz7kvzcpW6HV6G +XRG+rz+g+yA+O4Yq8kCbcuvg0h/dRHj7EYDtX43Plzn7ra3JawRbJzQLAJg3QB3p +AHFpb553fpdkbQxKSB+s1qf5oQ1de92qnBHAN3VVgS4Z9ljpHesKc7FnB4/sClaV +N8uL/VFe4jI2uQv4m0QUpFypzR8z+JLy4EKDExiQ9Z+BQpF/IFd03DerWX3r6wP0 +3GA1X9fUXaZ5Aifwjnx1/AI+4NnZy6ISeJYwddQLJYneFsfuFsYqADidfc5X8A0l +ETMAZPTYO5u+Zg1mcwVGY5fEuZrswp8+S0/jppatVgOEFMu97gOjOpqiTf/KT5XH +skSXheFP/Q0s1OP2LEflRDt/Tmlev7rvZKE1lkV6Y+Oa1JtTJcaLMiiChmSZl5Ki +m6P9ZJWTcfRuUmyhrGCVNuGNTivKQhldQTynlH5KRBjp0uPjDGUkVy2wPT4IaY/u +ElwXmuusVhpJ8c6m9T52Bo5NXerH5sZGuCu/1rOLYl+bMtps4jDum+liLxgTfTaG +VvnLa+vyhND6SMej4gtwTVAScgcE2H4uuVA6Rlqso0sCAwEAAaN1MHMwCQYDVR0T +BAIwADAOBgNVHQ8BAf8EBAMCBaAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwIwHQYD +VR0OBBYEFE9qmHF7NVXLJ8rpurJFau+C27mZMB8GA1UdIwQYMBaAFKAbUoQTBYbj +a9Od71prY343vDZNMA0GCSqGSIb3DQEBCwUAA4ICAQASteG44GPxwMI0y/dnSPLh +XKgRBfB6BPSOeL47q0Lehws5yynQKTAU5qEHNEzfdYNvHM3f4aY4/chHULE5qnKV +ZUh2h49Vmbu3qRTtd5CiSj9ZQqE10Zsojais7Y+N8NyL5NTBY596JIzr7fnoDYT7 +UxE+ahDeekpj9beGjkZ+tlNi7QH73PDq6g9TYMbtDci+fpbaixXwKXUe8HYQlQpX +ymmMPB1YDGRuoyh0vTJVnd6Bx1E6+juSh840siaZF0w5/FK+8WRofxnEvvTWZjJa +Kh8Dtr81lY0ciW8DF9d95iayC99Kp6ONWtfc7FLY5pEJNzTOMTd3YcdhGZVoutxh +44JrYZa34Nl+r8o8IEwgiaaOUZN4WNua9q/VLZfB2OKn8I4+rPmAKv8tIhgsLzNK +s/Hb+vqX9QsWqGFgUd8+Q5z3AL2gBqJB96+Mt7VjKV9ND+q2HxKOdu669pCs0Vcb +8RLdmJTCPnrl/1HXByR5kLe2K0jSG1hVvZywceX1zTMzcNWKnDA74ibg4azIOnhF +e3fPBtFoE1OZlDuGCc3ik3BuoVQdMbVV/a6aVbIjO+uweuFiyE0wd/6gG/fP7D1A +Vme8zbZTBZEuz3a41w77H8TaTLhe8O2fiSqtywNOn0YtPK6UraSTET7SGJQq5vJj +5aXaiSHg5THV7KZ1D9I6Lg== +-----END CERTIFICATE----- diff --git a/certs/client.der b/certs/client.der new file mode 100644 index 0000000..24e4b33 Binary files /dev/null and b/certs/client.der differ diff --git a/certs/client.key b/certs/client.key new file mode 100644 index 0000000..0754130 --- /dev/null +++ b/certs/client.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +VxKTyw6mBwq6/LXr+QQs/+IT2Sjg4A== +-----END PRIVATE KEY----- diff --git a/certs/client.key.der b/certs/client.key.der new file mode 100644 index 0000000..17f1d17 Binary files /dev/null and b/certs/client.key.der differ diff --git a/certs/client.p12 b/certs/client.p12 new file mode 100644 index 0000000..e68cd18 Binary files /dev/null and b/certs/client.p12 differ diff --git a/certs/ip6-server.der b/certs/ip6-server.der new file mode 100644 index 0000000..4e00a57 Binary files /dev/null and b/certs/ip6-server.der differ diff --git a/certs/ip6-server.key.der b/certs/ip6-server.key.der new file mode 100644 index 0000000..e2ba343 Binary files /dev/null and b/certs/ip6-server.key.der differ diff --git a/certs/server.cnf b/certs/server.cnf new file mode 100644 index 0000000..5afd139 --- /dev/null +++ b/certs/server.cnf @@ -0,0 +1,16 @@ +# server.cnf +[ req ] +prompt = no +distinguished_name = req_distinguished_name +req_extensions = req_ext + +[ req_distinguished_name ] +CN = localhost + +[ req_ext ] +subjectAltName = @alt_names + +[ alt_names ] +DNS.1 = localhost +IP.1 = 127.0.0.1 +# Add more DNS names or IPs as needed, e.g., DNS.2 = myapp.internal diff --git a/certs/server.crt b/certs/server.crt new file mode 100644 index 0000000..40b0916 --- /dev/null +++ b/certs/server.crt @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFEDCCAvigAwIBAgIUXNy9jnSXC73JGQ5ZPh1r0BK5+Y0wDQYJKoZIhvcNAQEL +BQAwEDEOMAwGA1UEAwwFTXkgQ0EwHhcNMjYwMTE0MDAxNjQwWhcNMzYwMTEyMDAx +NjQwWjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCXwudcfKePoE97PtA+wgDxzgDfk9KJhKhEDWJgW9GbSjbpudQ0 +j3P+7KKmOR7KkoW7fmiKcfXVVeMXDgz93vI+ViIXoQTO8YStWt/nuvClUqPbX1DR +583k5dS8E8jpamWfM2GNjgj/zIpI6T1MrtLOlQDk0jjiKUYLH26LY8zF422aNwri +wFS9bkTh0A8VHJ1A9LtX42TLjau9rccwUtltR3GyXRWewjfb2AgBWwP1Vmm39Z+k +gGWxDPecTQscw/5zBK+WbACRBU7IQyXj37d7o9ySqynEzga+xCvFezT6VsO2oASI +4K5uY8dKGpYibAs3R7u2vcHaNlyU53dDIwQSp83SqtrFzBLKtuDREdtGpwgBj9Av +4nlqmtwGBgmPgar03FUUxzUTVUyhbKOC06VjzD3g3DK0SBmVCqzHvi997ZJXXMIl +TT5pUVTM1VUMVtJGVydrgVpRA9CG6bVZ7Z1YZkzK2Iwf0agKAguWKgivXsavs4KV +BLmUvBIk29sRGchzoxT5Ms/nKs7jRMlG+PdY5BIWvTxg9koTDLfGLJ6T+3HTnIHR +K6xU4ac8v8ESNWz1bLbg2mhiFR1z1iFNhPgjPpw324+IxQ+D6MNav4gLqy7rlcaN +jTENV0817ncgbIwNd/ceXcVHCB/blzAa5MAko0r/HGQl3pe2r5+/OsxvCwIDAQAB +o14wXDAaBgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEwHQYDVR0OBBYEFPSbh5Ha +mGiURrS+lZQPyw2BdCq1MB8GA1UdIwQYMBaAFKAbUoQTBYbja9Od71prY343vDZN +MA0GCSqGSIb3DQEBCwUAA4ICAQA6fZhuxUKs2DGNAY59OmvRQ+wwpewGxLdLHI8H +IDqgGM2HCo/TMBB9YxaGZi+de3QYiEzdCbvvomzrISVaNcM6xuD28Y5ds4aAktAY +jHLn3QNXv4SLjSOdWGsVql0QpCIoyqKAjwavjFo/ESKKJ4br5MPRCbAI9XhUz5I9 +8qRNm1boxLbSbzXme4rMNxn/HbQyBGlXdN7ACXHIWhu0t9LM9SqipHKFNKoFXX1Q +cBfI3N+4bPcbYrgQAb04nylGHarXH7rmA8+0FQV62zxWzjg6V6cIdlPy2DATYlXV +5iAMtDpTmuQA2tmXQd5XBqsdsvHYUCNmcxb1tEB/cn8iveiufL8uFKwx/ftzvMBZ +pWLxpoUZIHhv9oZTfaoJP8S/uRGZsTUusC5PSNE9ZVr9PwGCmudaKTH6PU4ft1zN +LVpkEjOjzPrvPwmnn+hiDUeS+YaUkZLCXWPpuYj5i0Ilfwq3Oq7jWePydGvFLMPr +92b/93V3LhrvPKpl1NzGfZwMmNxp9DkPkhTNYfJHJ89P+2UT1xtYFSxB73IRy4Xj +1ZjuOEVPZLLD2rDRtw0t1lQIh+gvPiu43JwYjzXwOND3lADfiVtkM+IE0BlH/2fo +aA/PcfCUsFZii+fIZFu3WPZi50pqgSLdCGD/VBTZitbD6KceZFDgVfLyL1hLA1Hq +OiW0EQ== +-----END CERTIFICATE----- diff --git a/certs/server.der b/certs/server.der new file mode 100644 index 0000000..a3dc293 Binary files /dev/null and b/certs/server.der differ diff --git a/certs/server.key b/certs/server.key new file mode 100644 index 0000000..2650da9 --- /dev/null +++ b/certs/server.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +N7qFqsnFU1Py0bfCZCAlyt7fQH/qmx8= +-----END PRIVATE KEY----- diff --git a/certs/server.key.der b/certs/server.key.der new file mode 100644 index 0000000..7dd5ee4 Binary files /dev/null and b/certs/server.key.der differ diff --git a/docs/index.md b/docs/index.md index c25cc73..c94c998 100644 --- a/docs/index.md +++ b/docs/index.md @@ -9,10 +9,7 @@ permalink: /

EasyHttpMock

-[![crates.io](https://img.shields.io/crates/v/easyhttpmock?style=flat-square)](https://crates.io/crates/easyhttpmock) -[![Build Status](https://github.com/ararog/easyhttpmock/actions/workflows/rust.yml/badge.svg?event=push)](https://github.com/ararog/easyhttpmock/actions/workflows/rust.yml) -[![Documentation](https://docs.rs/easyhttpmock/badge.svg)](https://docs.rs/easyhttpmock/latest/easyhttpmock) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![Crates.io downloads](https://img.shields.io/crates/d/easyhttpmock)](https://crates.io/crates/easyhttpmock) [![crates.io](https://img.shields.io/crates/v/easyhttpmock?style=flat-square)](https://crates.io/crates/easyhttpmock) [![Build Status](https://github.com/ararog/easyhttpmock/actions/workflows/rust.yml/badge.svg?event=push)](https://github.com/ararog/easyhttpmock/actions/workflows/rust.yml) ![Crates.io MSRV](https://img.shields.io/crates/msrv/easyhttpmock) [![Documentation](https://docs.rs/easyhttpmock/badge.svg)](https://docs.rs/easyhttpmock/latest/easyhttpmock) [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/ararog/easyhttpmock/blob/main/LICENSE.md) [![codecov](https://codecov.io/gh/ararog/easyhttpmock/graph/badge.svg?token=T0HSBAPVSI)](https://codecov.io/gh/ararog/easyhttpmock) **EasyHttpMock** is a powerful yet simple HTTP mock server designed specifically for testing HTTP clients. Built on top of [VeTiS](https://github.com/ararog/vetis), it provides a clean, intuitive API for creating realistic mock endpoints that simulate real-world API behavior, making your testing workflow faster and more reliable. @@ -37,25 +34,25 @@ easyhttpmock = { version = "0.0.9" } Basic usage: ```rust -use bytes::Bytes; use http::StatusCode; -use http_body_util::Full; -use hyper::Response; use easyhttpmock::{ - config::EasyHttpMockConfig, - server::{adapters::vetis_adapter::VetisServerAdapter, PortGenerator}, EasyHttpMock, -}; - -use deboa::{cert::ContentEncoding, request::DeboaRequest, Client}; - -use vetis::{ + config::EasyHttpMockConfig, server::{ - config::{SecurityConfig, ServerConfig}, + PortGenerator, + adapters::vetis_adapter::{VetisAdapter, VetisAdapterConfig}, }, }; +use deboa::{ + Client, + cert::{Certificate, ContentEncoding}, + request::DeboaRequest, +}; + +use vetis::Response; + pub const CA_CERT: &[u8] = include_bytes!("../certs/ca.der"); pub const CA_CERT_PEM: &[u8] = include_bytes!("../certs/ca.crt"); @@ -64,22 +61,19 @@ pub const SERVER_KEY: &[u8] = include_bytes!("../certs/server.key.der"); #[tokio::main] async fn main() -> Result<(), Box> { - let tls_config = SecurityConfig::builder() - .cert(SERVER_CERT.to_vec()) - .key(SERVER_KEY.to_vec()) - .build(); - - let vetis_config = ServerConfig::builder() - .security(tls_config) + let vetis_adapter_config = VetisAdapterConfig::builder() + .interface("0.0.0.0") .with_random_port() + .cert(Some(SERVER_CERT.to_vec())) + .key(Some(SERVER_KEY.to_vec())) + .ca(Some(CA_CERT.to_vec())) .build(); - let config = EasyHttpMockConfig::::builder() - .server_config(vetis_config) + let config = EasyHttpMockConfig::::builder() + .server_config(vetis_adapter_config) .build(); - let mut server = EasyHttpMock::new(config); - #[allow(unused_must_use)] + let mut server = EasyHttpMock::new(config)?; let result = server .start(|_| async move { Ok(Response::new(Full::new(Bytes::from("Hello World")))) @@ -109,6 +103,7 @@ async fn main() -> Result<(), Box> { .await?; Ok(()) +} ``` ## Examples diff --git a/docs/llms.txt b/docs/llms.txt index 66d74e4..81181de 100644 --- a/docs/llms.txt +++ b/docs/llms.txt @@ -9,28 +9,28 @@ easyhttpmock = { version = "0.0.1", features = ["http1", "tokio-rt", "tokio-rust-tls"] } ``` -## 💡 Usage Example +## Usage Example ```rust -use bytes::Bytes; use http::StatusCode; -use http_body_util::Full; -use hyper::Response; use easyhttpmock::{ - config::EasyHttpMockConfig, - server::{adapters::vetis_adapter::VetisServerAdapter, PortGenerator}, EasyHttpMock, -}; - -use deboa::{cert::ContentEncoding, request::DeboaRequest, Client}; - -use vetis::{ + config::EasyHttpMockConfig, server::{ - config::{SecurityConfig, ServerConfig}, + PortGenerator, + adapters::vetis_adapter::{VetisAdapter, VetisAdapterConfig}, }, }; +use deboa::{ + Client, + cert::{Certificate, ContentEncoding}, + request::DeboaRequest, +}; + +use vetis::Response; + pub const CA_CERT: &[u8] = include_bytes!("../certs/ca.der"); pub const CA_CERT_PEM: &[u8] = include_bytes!("../certs/ca.crt"); @@ -39,22 +39,19 @@ pub const SERVER_KEY: &[u8] = include_bytes!("../certs/server.key.der"); #[tokio::main] async fn main() -> Result<(), Box> { - let tls_config = SecurityConfig::builder() - .cert(SERVER_CERT.to_vec()) - .key(SERVER_KEY.to_vec()) - .build(); - - let vetis_config = ServerConfig::builder() - .security(tls_config) + let vetis_adapter_config = VetisAdapterConfig::builder() + .interface("0.0.0.0") .with_random_port() + .cert(Some(SERVER_CERT.to_vec())) + .key(Some(SERVER_KEY.to_vec())) + .ca(Some(CA_CERT.to_vec())) .build(); - let config = EasyHttpMockConfig::::builder() - .server_config(vetis_config) + let config = EasyHttpMockConfig::::builder() + .server_config(vetis_adapter_config) .build(); - let mut server = EasyHttpMock::new(config); - #[allow(unused_must_use)] + let mut server = EasyHttpMock::new(config)?; let result = server .start(|_| async move { Ok(Response::new(Full::new(Bytes::from("Hello World")))) @@ -84,16 +81,17 @@ async fn main() -> Result<(), Box> { .await?; Ok(()) +} ``` ## Features -- **🎯 Testing-Focused**: Purpose-built for HTTP client testing scenarios -- **⚡ Lightning Fast**: Powered by VeTiS for optimal performance -- **🔧 Flexible Runtime**: Choose between Tokio or Smol async runtimes -- **🌐 Full Protocol Support**: HTTP/1, HTTP/2, and HTTP/3 compatibility -- **🛡️ Secure Testing**: Built-in TLS support for HTTPS endpoint testing -- **📦 Minimal Dependencies**: Lightweight footprint for your test suite +- ** Testing-Focused**: Purpose-built for HTTP client testing scenarios +- ** Lightning Fast**: Powered by VeTiS for optimal performance +- ** Flexible Runtime**: Choose between Tokio or Smol async runtimes +- ** Full Protocol Support**: HTTP/1, HTTP/2, and HTTP/3 compatibility +- ** Secure Testing**: Built-in TLS support for HTTPS endpoint testing +- ** Minimal Dependencies**: Lightweight footprint for your test suite ## Blog Posts diff --git a/easyhttpmock-vetis-smol/Cargo.toml b/easyhttpmock-vetis-smol/Cargo.toml new file mode 100644 index 0000000..acab3f1 --- /dev/null +++ b/easyhttpmock-vetis-smol/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "easyhttpmock-vetis-smol" +version = "0.1.0-beta.1" +edition = "2021" +authors = ["Rogerio Araújo "] +repository = "https://github.com/ararog/easyhttpmock" +homepage = "https://github.com/ararog/easyhttpmock" +description = "EasyHttpMock adapter for Vetis HTTP server using smol runtime." +readme = "README.md" +license = "MIT OR Apache-2.0" +keywords = ["http", "mock", "testing"] +publish = true +rust-version = "1.75.0" + +[package.metadata.docs.rs] +all-features = true + +[features] +http1 = ["vetis-smol/http1"] +http2 = ["vetis-smol/http2"] +http3 = ["vetis-smol/http3"] + +rust-tls = ["vetis-smol/rust-tls"] + +[dependencies] +easyhttpmock = { path = "../easyhttpmock" } +googletest = "0.14.2" +http = "1.4.0" +macro_rules_attribute = "0.2.2" +rand = "0.9.2" +smol-macros = "0.1.1" +vetis-smol = { path = "../../vetis/vetis-smol", features = [ + "http2", + "rust-tls", +], default-features = false } + +[dev-dependencies] +smol = { version = "2.0.2", default-features = false } +smol-macros = { version = "0.1.1", default-features = false } diff --git a/easyhttpmock-vetis-smol/README.md b/easyhttpmock-vetis-smol/README.md new file mode 100644 index 0000000..e69de29 diff --git a/easyhttpmock-vetis-smol/src/lib.rs b/easyhttpmock-vetis-smol/src/lib.rs new file mode 100644 index 0000000..87181e8 --- /dev/null +++ b/easyhttpmock-vetis-smol/src/lib.rs @@ -0,0 +1,8 @@ +#![doc = include_str!("../README.md")] +#![deny(missing_docs)] +/// Vetis smol adapter module +pub mod vetis_adapter; +pub use easyhttpmock::*; +pub use vetis_smol::{handler_fn, Protocol}; +#[cfg(test)] +mod tests; diff --git a/easyhttpmock-vetis-smol/src/tests/mod.rs b/easyhttpmock-vetis-smol/src/tests/mod.rs new file mode 100644 index 0000000..99f2395 --- /dev/null +++ b/easyhttpmock-vetis-smol/src/tests/mod.rs @@ -0,0 +1,63 @@ +use std::error::Error; + +use easyhttpmock::{ + config::EasyHttpMockConfig, + mock::{MethodExt, Mock, StatusCodeExt}, + server::PortGenerator, + EasyHttpMock, +}; +use http::{Method, StatusCode}; + +use crate::vetis_adapter::{VetisAdapter, VetisAdapterConfig}; +use vetis_smol::Protocol; + +use macro_rules_attribute::apply; +use smol_macros::test; + +const CA_CERT: &[u8] = include_bytes!("../../../certs/ca.der"); + +const SERVER_CERT: &[u8] = include_bytes!("../../../certs/server.der"); +const SERVER_KEY: &[u8] = include_bytes!("../../../certs/server.key.der"); + +#[apply(test!)] +async fn test_mock_request() -> Result<(), Box> { + let server_cert = SERVER_CERT; + let server_key = SERVER_KEY; + + let vetis_adapter_config = VetisAdapterConfig::builder() + .hostname(Some("localhost".to_string())) + .interface("0.0.0.0") + .protocol(Protocol::Http2) + .with_random_port() + .cert(Some(server_cert.to_vec())) + .key(Some(server_key.to_vec())) + .ca(Some(CA_CERT.to_vec())) + .build(); + + let config = EasyHttpMockConfig::::builder() + .server_config(vetis_adapter_config) + .build(); + + let server = EasyHttpMock::new(config); + let mut server = match server { + Ok(server) => server, + Err(err) => { + panic!("Failed to create mock server: {}", err); + } + }; + + let mock = Mock::of( + Method::GET + .has() + .path("/test") + .will_return( + StatusCode::OK + .respond() + .with_body(b"teste"), + ), + ); + + server.register_mock(mock); + + Ok(()) +} diff --git a/easyhttpmock-vetis-smol/src/vetis_adapter.rs b/easyhttpmock-vetis-smol/src/vetis_adapter.rs new file mode 100644 index 0000000..69d6dba --- /dev/null +++ b/easyhttpmock-vetis-smol/src/vetis_adapter.rs @@ -0,0 +1,453 @@ +use std::sync::{Arc, RwLock}; + +use vetis_smol::{ + handler_fn, + http::Response, + virtual_host::{path::HandlerPath, VirtualHost}, + Protocol, ServerConfig, Vetis, +}; + +use easyhttpmock::{ + errors::{EasyHttpMockError, MockError, ServerError}, + mock::{ActualRequest, Mock}, + server::{PortGenerator, ServerAdapter}, +}; + +/// Builder for VetisAdapterConfig +pub struct VetisAdapterConfigBuilder { + hostname: Option, + interface: String, + protocol: Protocol, + port: u16, + cert: Option>, + key: Option>, + ca: Option>, +} + +impl VetisAdapterConfigBuilder { + /// Sets the hostname for the server. + /// + /// # Arguments + /// * `hostname` - The hostname to set. + /// + /// # Returns + /// A new `VetisAdapterConfigBuilder` instance with the hostname set. + pub fn hostname(mut self, hostname: Option) -> Self { + self.hostname = hostname; + self + } + + /// Sets the interface for the server. + /// + /// # Arguments + /// * `interface` - The interface to set. + /// + /// # Returns + /// A new `VetisAdapterConfigBuilder` instance with the interface set. + pub fn interface(mut self, interface: &str) -> Self { + self.interface = interface.to_string(); + self + } + + /// Sets the protocol for the server. + /// + /// # Arguments + /// * `protocol` - The protocol to set. + /// + /// # Returns + /// A new `VetisAdapterConfigBuilder` instance with the protocol set. + pub fn protocol(mut self, protocol: Protocol) -> Self { + self.protocol = protocol; + self + } + + /// Sets the port for the server. + /// + /// # Arguments + /// * `port` - The port to set. + /// + /// # Returns + /// A new `VetisAdapterConfigBuilder` instance with the port set. + pub fn port(mut self, port: u16) -> Self { + self.port = port; + self + } + + /// Sets the certificate for the server. + /// + /// # Arguments + /// * `cert` - The certificate to set. + /// + /// # Returns + /// A new `VetisAdapterConfigBuilder` instance with the certificate set. + pub fn cert(mut self, cert: Option>) -> Self { + self.cert = cert; + self + } + + /// Sets the key for the server. + /// + /// # Arguments + /// * `key` - The key to set. + /// + /// # Returns + /// A new `VetisAdapterConfigBuilder` instance with the key set. + pub fn key(mut self, key: Option>) -> Self { + self.key = key; + self + } + + /// Sets the CA certificate for the server. + /// + /// # Arguments + /// * `ca` - The CA certificate to set. + /// + /// # Returns + /// A new `VetisAdapterConfigBuilder` instance with the CA certificate set. + pub fn ca(mut self, ca: Option>) -> Self { + self.ca = ca; + self + } + + /// Builds the VetisAdapterConfig from the builder. + /// + /// # Returns + /// A new `VetisAdapterConfig` instance. + pub fn build(self) -> VetisAdapterConfig { + VetisAdapterConfig { + hostname: self.hostname, + interface: self.interface, + protocol: self.protocol, + port: self.port, + cert: self.cert, + key: self.key, + ca: self.ca, + } + } +} + +/// Configuration for the Vetis adapter. +#[derive(Clone)] +pub struct VetisAdapterConfig { + hostname: Option, + interface: String, + protocol: Protocol, + port: u16, + cert: Option>, + key: Option>, + ca: Option>, +} + +impl Default for VetisAdapterConfig { + /// Creates a default configuration for the Vetis adapter. + /// + /// This function sets up a basic server configuration with: + /// - Interface: "0.0.0.0" + /// - Port: 80 + /// - No TLS certificates (HTTP only) + /// + /// # Returns + /// A default `VetisAdapterConfig` instance. + fn default() -> Self { + Self { + hostname: None, + interface: "0.0.0.0".into(), + protocol: Protocol::Http1, + port: 80, + cert: None, + key: None, + ca: None, + } + } +} + +impl VetisAdapterConfig { + /// Creates a new builder for the Vetis adapter configuration. + /// + /// This function sets up a basic server configuration with: + /// - Interface: "0.0.0.0" + /// - Port: 80 + /// - No TLS certificates (HTTP only) + /// + /// # Returns + /// A new `VetisAdapterConfigBuilder` instance. + pub fn builder() -> VetisAdapterConfigBuilder { + VetisAdapterConfigBuilder { + hostname: None, + interface: "0.0.0.0".into(), + protocol: Protocol::Http1, + port: 80, + cert: None, + key: None, + ca: None, + } + } + + /// Returns the hostname of the server. + /// + /// # Returns + /// The hostname of the server. + pub fn hostname(&self) -> &Option { + &self.hostname + } + + /// Returns the interface of the server. + /// + /// # Returns + /// The interface of the server. + pub fn interface(&self) -> &str { + &self.interface + } + + /// Returns the port of the server. + /// + /// # Returns + /// The port of the server. + pub fn port(&self) -> u16 { + self.port + } + + /// Returns the certificate of the server. + /// + /// # Returns + /// The certificate of the server. + pub fn cert(&self) -> &Option> { + &self.cert + } + + /// Returns the key of the server. + /// + /// # Returns + /// The key of the server. + pub fn key(&self) -> &Option> { + &self.key + } + + /// Returns the CA certificate of the server. + /// + /// # Returns + /// The CA certificate of the server. + pub fn ca(&self) -> &Option> { + &self.ca + } +} + +impl From for ServerConfig { + fn from(config: VetisAdapterConfig) -> Self { + let listener_config = vetis_smol::ListenerConfig::builder() + .interface(&config.interface) + .protocol(config.protocol) + .port(config.port) + .build() + .expect("Failed to build listener config"); + ServerConfig::builder() + .add_listener(listener_config) + .build() + .expect("Failed to build server config") + } +} + +/// Vetis adapter implementation +pub struct VetisAdapter { + server: Vetis, + config: VetisAdapterConfig, + mock: Option>>, +} + +impl PortGenerator for VetisAdapterConfigBuilder { + fn with_random_port(self) -> Self { + let port = rand::random_range(9000..65535); + self.port(port) + } +} + +impl ServerAdapter for VetisAdapter { + type Config = VetisAdapterConfig; + + /// Creates a new VetisAdapter instance. + /// + /// # Arguments + /// * `config` - The configuration for the adapter. + /// + /// # Returns + /// A new `VetisAdapter` instance. + fn new(config: Self::Config) -> Result { + let vetis_config = config + .clone() + .into(); + + let server = Vetis::new(vetis_config); + + Ok(Self { server, config, mock: None }) + } + + /// Returns the hostname of the server. + /// + /// # Returns + /// The hostname of the server. + fn hostname(&self) -> String { + self.config + .hostname() + .clone() + .unwrap_or_else(|| "localhost".to_string()) + } + + /// Returns the base URL of the server. + /// + /// # Returns + /// The base URL of the server. + fn base_url(&self) -> String { + let hostname = self.hostname(); + + if self + .config + .cert + .is_some() + { + format!("https://{}:{}", hostname, self.config.port()) + } else { + format!("http://{}:{}", hostname, self.config.port()) + } + } + + /// Returns the configuration of the server. + /// + /// # Returns + /// The configuration of the server. + fn config(&self) -> &Self::Config { + &self.config + } + + /// Sets the mock to handle incoming requests. + /// + /// # Arguments + /// + /// * `mock` - The mock to handle incoming requests. + /// + /// # Returns + /// + /// * `Result<(), EasyHttpMockError>` - The result of the operation. + /// + fn register_mock(&mut self, mock: Mock) { + self.mock = Some(Arc::new(RwLock::new(mock))); + } + + /// Starts the server with the given handler. + /// + /// # Arguments + /// + /// * `handler` - The handler to use for the server. + /// + /// # Returns + /// + /// A result indicating whether the server started successfully or a `EasyHttpMockError` if it failed. + /// + async fn start(&mut self) -> Result<(), EasyHttpMockError> { + let mock = match self.mock.as_ref() { + Some(mocker) => mocker, + None => return Err(MockError::Notfound.into()), + }; + + let mock_clone = mock.clone(); + let path = HandlerPath::builder() + .uri("/") + .handler(handler_fn(move |request| { + // Since handler function is defined here, we need to clone the mocker + // to move it into the async block + let mock = mock_clone.clone(); + async move { + let (parts, _body) = request.into_parts(); + + let mut mock_write_guard = mock + .write() + .unwrap(); + + mock_write_guard.match_with( + ActualRequest::builder() + .path(parts.uri.path()) + .method(parts.method) + .headers(parts.headers) + .build(), + ); + + mock_write_guard.report_call(); + + drop(mock_write_guard); + + let mock_read_guard = mock.read().unwrap(); + let respond = mock_read_guard + .request() + .respond(); + + Ok(Response::builder() + .status(respond.status_code()) + .bytes(&respond.body())) + } + })) + .build(); + + let hostname = self.hostname(); + + let host_config = vetis_smol::VirtualHostConfig::builder() + .hostname(&hostname) + .root_directory(".") + .port(self.config.port()); + + let host_config = if let Some(((cert, key), ca)) = self + .config + .cert + .as_ref() + .zip( + self.config + .key + .as_ref(), + ) + .zip( + self.config + .ca + .as_ref(), + ) { + host_config.security( + vetis_smol::SecurityConfig::builder() + .cert_from_bytes(cert.clone()) + .key_from_bytes(key.clone()) + .ca_cert_from_bytes(ca.clone()) + .build() + .map_err(|e| EasyHttpMockError::Server(ServerError::Config(e.to_string())))?, + ) + } else { + host_config + }; + + let host_config = host_config + .build() + .map_err(|e| EasyHttpMockError::Server(ServerError::Creation(e.to_string())))?; + + let mut host = VirtualHost::new(host_config); + if let Err(e) = path { + return Err(EasyHttpMockError::Server(ServerError::Creation(e.to_string()))); + } + + host.add_path(path.unwrap()); + + self.server + .add_virtual_host(host) + .await; + + self.server + .start() + .await + .map_err(|e| EasyHttpMockError::Server(ServerError::Start(e.to_string()))) + } + + /// Stops the server. + /// + /// # Returns + /// A result indicating whether the server stopped successfully. + async fn stop(&mut self) -> Result<(), EasyHttpMockError> { + self.server + .stop() + .await + .map_err(|e| EasyHttpMockError::Server(ServerError::Stop(e.to_string()))) + } +} diff --git a/easyhttpmock-vetis-tokio/Cargo.toml b/easyhttpmock-vetis-tokio/Cargo.toml new file mode 100644 index 0000000..a55be40 --- /dev/null +++ b/easyhttpmock-vetis-tokio/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "easyhttpmock-vetis-tokio" +version = "0.1.0-beta.1" +edition = "2021" +authors = ["Rogerio Araújo "] +repository = "https://github.com/ararog/easyhttpmock" +homepage = "https://github.com/ararog/easyhttpmock" +description = "EasyHttpMock adapter for Vetis HTTP server using tokio runtime." +readme = "README.md" +license = "MIT OR Apache-2.0" +keywords = ["http", "mock", "testing"] +publish = true +rust-version = "1.75.0" + +[package.metadata.docs.rs] +all-features = true + +[features] +http1 = ["vetis-tokio/http1"] +http2 = ["vetis-tokio/http2"] +http3 = ["vetis-tokio/http3"] + +rust-tls = ["vetis-tokio/rust-tls"] + +[dependencies] +easyhttpmock = { path = "../easyhttpmock" } +googletest = "0.14.2" +http = "1.4.0" +rand = "0.9.2" +tokio = "1.50.0" +vetis-tokio = { path = "../../vetis/vetis-tokio", features = [ + "http2", + "rust-tls", +], default-features = false } + +[dev-dependencies] +deboa = { path = "../../deboa/deboa" } +deboa-tokio = { path = "../../deboa/deboa-tokio" } +tokio = { version = "1.50.0", default-features = false, features = ["macros"] } diff --git a/easyhttpmock-vetis-tokio/README.md b/easyhttpmock-vetis-tokio/README.md new file mode 100644 index 0000000..e69de29 diff --git a/easyhttpmock-vetis-tokio/src/lib.rs b/easyhttpmock-vetis-tokio/src/lib.rs new file mode 100644 index 0000000..1a51e1c --- /dev/null +++ b/easyhttpmock-vetis-tokio/src/lib.rs @@ -0,0 +1,8 @@ +#![doc = include_str!("../README.md")] +#![deny(missing_docs)] +/// Vetis tokio adapter module +pub mod vetis_adapter; +pub use easyhttpmock::*; +pub use vetis_tokio::Protocol; +#[cfg(test)] +mod tests; diff --git a/easyhttpmock-vetis-tokio/src/tests/mod.rs b/easyhttpmock-vetis-tokio/src/tests/mod.rs new file mode 100644 index 0000000..c41aff2 --- /dev/null +++ b/easyhttpmock-vetis-tokio/src/tests/mod.rs @@ -0,0 +1,60 @@ +use std::error::Error; + +use easyhttpmock::{ + config::EasyHttpMockConfig, + mock::{MethodExt, Mock, StatusCodeExt}, + server::PortGenerator, + EasyHttpMock, +}; +use http::{Method, StatusCode}; + +use crate::vetis_adapter::{VetisAdapter, VetisAdapterConfig}; +use vetis_tokio::Protocol; + +const CA_CERT: &[u8] = include_bytes!("../../../certs/ca.der"); + +const SERVER_CERT: &[u8] = include_bytes!("../../../certs/server.der"); +const SERVER_KEY: &[u8] = include_bytes!("../../../certs/server.key.der"); + +#[tokio::test] +async fn test_mock_request() -> Result<(), Box> { + let server_cert = SERVER_CERT; + let server_key = SERVER_KEY; + + let vetis_adapter_config = VetisAdapterConfig::builder() + .hostname(Some("localhost".to_string())) + .interface("0.0.0.0") + .protocol(Protocol::Http2) + .with_random_port() + .cert(Some(server_cert.to_vec())) + .key(Some(server_key.to_vec())) + .ca(Some(CA_CERT.to_vec())) + .build(); + + let config = EasyHttpMockConfig::::builder() + .server_config(vetis_adapter_config) + .build(); + + let server = EasyHttpMock::new(config); + let mut server = match server { + Ok(server) => server, + Err(err) => { + panic!("Failed to create mock server: {}", err); + } + }; + + let mock = Mock::of( + Method::GET + .has() + .path("/test") + .will_return( + StatusCode::OK + .respond() + .with_body(b"teste"), + ), + ); + + server.register_mock(mock); + + Ok(()) +} diff --git a/easyhttpmock-vetis-tokio/src/vetis_adapter.rs b/easyhttpmock-vetis-tokio/src/vetis_adapter.rs new file mode 100644 index 0000000..170ce45 --- /dev/null +++ b/easyhttpmock-vetis-tokio/src/vetis_adapter.rs @@ -0,0 +1,453 @@ +use std::sync::{Arc, RwLock}; + +use vetis_tokio::{ + handler_fn, + http::Response, + virtual_host::{path::HandlerPath, VirtualHost}, + Protocol, ServerConfig, Vetis, +}; + +use easyhttpmock::{ + errors::{EasyHttpMockError, MockError, ServerError}, + mock::{ActualRequest, Mock}, + server::{PortGenerator, ServerAdapter}, +}; + +/// Builder for VetisAdapterConfig +pub struct VetisAdapterConfigBuilder { + hostname: Option, + interface: String, + protocol: Protocol, + port: u16, + cert: Option>, + key: Option>, + ca: Option>, +} + +impl VetisAdapterConfigBuilder { + /// Sets the hostname for the server. + /// + /// # Arguments + /// * `hostname` - The hostname to set. + /// + /// # Returns + /// A new `VetisAdapterConfigBuilder` instance with the hostname set. + pub fn hostname(mut self, hostname: Option) -> Self { + self.hostname = hostname; + self + } + + /// Sets the interface for the server. + /// + /// # Arguments + /// * `interface` - The interface to set. + /// + /// # Returns + /// A new `VetisAdapterConfigBuilder` instance with the interface set. + pub fn interface(mut self, interface: &str) -> Self { + self.interface = interface.to_string(); + self + } + + /// Sets the protocol for the server. + /// + /// # Arguments + /// * `protocol` - The protocol to set. + /// + /// # Returns + /// A new `VetisAdapterConfigBuilder` instance with the protocol set. + pub fn protocol(mut self, protocol: Protocol) -> Self { + self.protocol = protocol; + self + } + + /// Sets the port for the server. + /// + /// # Arguments + /// * `port` - The port to set. + /// + /// # Returns + /// A new `VetisAdapterConfigBuilder` instance with the port set. + pub fn port(mut self, port: u16) -> Self { + self.port = port; + self + } + + /// Sets the certificate for the server. + /// + /// # Arguments + /// * `cert` - The certificate to set. + /// + /// # Returns + /// A new `VetisAdapterConfigBuilder` instance with the certificate set. + pub fn cert(mut self, cert: Option>) -> Self { + self.cert = cert; + self + } + + /// Sets the key for the server. + /// + /// # Arguments + /// * `key` - The key to set. + /// + /// # Returns + /// A new `VetisAdapterConfigBuilder` instance with the key set. + pub fn key(mut self, key: Option>) -> Self { + self.key = key; + self + } + + /// Sets the CA certificate for the server. + /// + /// # Arguments + /// * `ca` - The CA certificate to set. + /// + /// # Returns + /// A new `VetisAdapterConfigBuilder` instance with the CA certificate set. + pub fn ca(mut self, ca: Option>) -> Self { + self.ca = ca; + self + } + + /// Builds the VetisAdapterConfig from the builder. + /// + /// # Returns + /// A new `VetisAdapterConfig` instance. + pub fn build(self) -> VetisAdapterConfig { + VetisAdapterConfig { + hostname: self.hostname, + interface: self.interface, + protocol: self.protocol, + port: self.port, + cert: self.cert, + key: self.key, + ca: self.ca, + } + } +} + +/// Configuration for the Vetis adapter. +#[derive(Clone)] +pub struct VetisAdapterConfig { + hostname: Option, + interface: String, + protocol: Protocol, + port: u16, + cert: Option>, + key: Option>, + ca: Option>, +} + +impl Default for VetisAdapterConfig { + /// Creates a default configuration for the Vetis adapter. + /// + /// This function sets up a basic server configuration with: + /// - Interface: "0.0.0.0" + /// - Port: 80 + /// - No TLS certificates (HTTP only) + /// + /// # Returns + /// A default `VetisAdapterConfig` instance. + fn default() -> Self { + Self { + hostname: None, + interface: "0.0.0.0".into(), + protocol: Protocol::Http1, + port: 80, + cert: None, + key: None, + ca: None, + } + } +} + +impl VetisAdapterConfig { + /// Creates a new builder for the Vetis adapter configuration. + /// + /// This function sets up a basic server configuration with: + /// - Interface: "0.0.0.0" + /// - Port: 80 + /// - No TLS certificates (HTTP only) + /// + /// # Returns + /// A new `VetisAdapterConfigBuilder` instance. + pub fn builder() -> VetisAdapterConfigBuilder { + VetisAdapterConfigBuilder { + hostname: None, + interface: "0.0.0.0".into(), + protocol: Protocol::Http1, + port: 80, + cert: None, + key: None, + ca: None, + } + } + + /// Returns the hostname of the server. + /// + /// # Returns + /// The hostname of the server. + pub fn hostname(&self) -> &Option { + &self.hostname + } + + /// Returns the interface of the server. + /// + /// # Returns + /// The interface of the server. + pub fn interface(&self) -> &str { + &self.interface + } + + /// Returns the port of the server. + /// + /// # Returns + /// The port of the server. + pub fn port(&self) -> u16 { + self.port + } + + /// Returns the certificate of the server. + /// + /// # Returns + /// The certificate of the server. + pub fn cert(&self) -> &Option> { + &self.cert + } + + /// Returns the key of the server. + /// + /// # Returns + /// The key of the server. + pub fn key(&self) -> &Option> { + &self.key + } + + /// Returns the CA certificate of the server. + /// + /// # Returns + /// The CA certificate of the server. + pub fn ca(&self) -> &Option> { + &self.ca + } +} + +impl From for ServerConfig { + fn from(config: VetisAdapterConfig) -> Self { + let listener_config = vetis_tokio::ListenerConfig::builder() + .interface(&config.interface) + .protocol(config.protocol) + .port(config.port) + .build() + .expect("Failed to build listener config"); + ServerConfig::builder() + .add_listener(listener_config) + .build() + .expect("Failed to build server config") + } +} + +/// Vetis adapter implementation +pub struct VetisAdapter { + server: Vetis, + config: VetisAdapterConfig, + mock: Option>>, +} + +impl PortGenerator for VetisAdapterConfigBuilder { + fn with_random_port(self) -> Self { + let port = rand::random_range(9000..65535); + self.port(port) + } +} + +impl ServerAdapter for VetisAdapter { + type Config = VetisAdapterConfig; + + /// Creates a new VetisAdapter instance. + /// + /// # Arguments + /// * `config` - The configuration for the adapter. + /// + /// # Returns + /// A new `VetisAdapter` instance. + fn new(config: Self::Config) -> Result { + let vetis_config = config + .clone() + .into(); + + let server = Vetis::new(vetis_config); + + Ok(Self { server, config, mock: None }) + } + + /// Returns the hostname of the server. + /// + /// # Returns + /// The hostname of the server. + fn hostname(&self) -> String { + self.config + .hostname() + .clone() + .unwrap_or_else(|| "localhost".to_string()) + } + + /// Returns the base URL of the server. + /// + /// # Returns + /// The base URL of the server. + fn base_url(&self) -> String { + let hostname = self.hostname(); + + if self + .config + .cert + .is_some() + { + format!("https://{}:{}", hostname, self.config.port()) + } else { + format!("http://{}:{}", hostname, self.config.port()) + } + } + + /// Returns the configuration of the server. + /// + /// # Returns + /// The configuration of the server. + fn config(&self) -> &Self::Config { + &self.config + } + + /// Sets the mock to handle incoming requests. + /// + /// # Arguments + /// + /// * `mock` - The mock to handle incoming requests. + /// + /// # Returns + /// + /// * `Result<(), EasyHttpMockError>` - The result of the operation. + /// + fn register_mock(&mut self, mock: Mock) { + self.mock = Some(Arc::new(RwLock::new(mock))); + } + + /// Starts the server with the given handler. + /// + /// # Arguments + /// + /// * `handler` - The handler to use for the server. + /// + /// # Returns + /// + /// A result indicating whether the server started successfully or a `EasyHttpMockError` if it failed. + /// + async fn start(&mut self) -> Result<(), EasyHttpMockError> { + let mock = match self.mock.as_ref() { + Some(mocker) => mocker, + None => return Err(MockError::Notfound.into()), + }; + + let mock_clone = mock.clone(); + let path = HandlerPath::builder() + .uri("/") + .handler(handler_fn(move |request| { + // Since handler function is defined here, we need to clone the mocker + // to move it into the async block + let mock = mock_clone.clone(); + async move { + let (parts, _body) = request.into_parts(); + + let mut mock_write_guard = mock + .write() + .unwrap(); + + mock_write_guard.match_with( + ActualRequest::builder() + .path(parts.uri.path()) + .method(parts.method) + .headers(parts.headers) + .build(), + ); + + mock_write_guard.report_call(); + + drop(mock_write_guard); + + let mock_read_guard = mock.read().unwrap(); + let respond = mock_read_guard + .request() + .respond(); + + Ok(Response::builder() + .status(respond.status_code()) + .bytes(&respond.body())) + } + })) + .build(); + + let hostname = self.hostname(); + + let host_config = vetis_tokio::VirtualHostConfig::builder() + .hostname(&hostname) + .root_directory(".") + .port(self.config.port()); + + let host_config = if let Some(((cert, key), ca)) = self + .config + .cert + .as_ref() + .zip( + self.config + .key + .as_ref(), + ) + .zip( + self.config + .ca + .as_ref(), + ) { + host_config.security( + vetis_tokio::SecurityConfig::builder() + .cert_from_bytes(cert.clone()) + .key_from_bytes(key.clone()) + .ca_cert_from_bytes(ca.clone()) + .build() + .map_err(|e| EasyHttpMockError::Server(ServerError::Config(e.to_string())))?, + ) + } else { + host_config + }; + + let host_config = host_config + .build() + .map_err(|e| EasyHttpMockError::Server(ServerError::Creation(e.to_string())))?; + + let mut host = VirtualHost::new(host_config); + if let Err(e) = path { + return Err(EasyHttpMockError::Server(ServerError::Creation(e.to_string()))); + } + + host.add_path(path.unwrap()); + + self.server + .add_virtual_host(host) + .await; + + self.server + .start() + .await + .map_err(|e| EasyHttpMockError::Server(ServerError::Start(e.to_string()))) + } + + /// Stops the server. + /// + /// # Returns + /// A result indicating whether the server stopped successfully. + async fn stop(&mut self) -> Result<(), EasyHttpMockError> { + self.server + .stop() + .await + .map_err(|e| EasyHttpMockError::Server(ServerError::Stop(e.to_string()))) + } +} diff --git a/easyhttpmock/Cargo.toml b/easyhttpmock/Cargo.toml new file mode 100644 index 0000000..345466f --- /dev/null +++ b/easyhttpmock/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "easyhttpmock" +version = "0.1.3-beta.7" +edition = "2021" +authors = ["Rogerio Araújo "] +repository = "https://github.com/ararog/easyhttpmock" +homepage = "https://github.com/ararog/easyhttpmock" +description = "EasyHttpMock is a simple HTTP mock server for testing HTTP clients." +readme = "README.md" +license = "MIT OR Apache-2.0" +keywords = ["http", "mock", "testing"] +publish = true +rust-version = "1.75.0" + +[package.metadata.docs.rs] +all-features = true + +[features] + +[dependencies] +bytes = "1.11.0" +googletest = "0.14.2" +http = "1.4.0" +http-body-util = "0.1.3" +hyper = { version = "1.8.1", features = ["full"] } +hyper-util = "0.1.9" +rand = "0.10.0" +thiserror = "2.0.17" diff --git a/easyhttpmock/README.md b/easyhttpmock/README.md new file mode 100644 index 0000000..e69de29 diff --git a/src/config.rs b/easyhttpmock/src/config.rs similarity index 52% rename from src/config.rs rename to easyhttpmock/src/config.rs index df4b8a7..2bfcd0b 100644 --- a/src/config.rs +++ b/easyhttpmock/src/config.rs @@ -1,10 +1,13 @@ use crate::server::ServerAdapter; +/// A easyhttpmock configuration builder for server adapter pub struct EasyHttpMockConfigBuilder where S: ServerAdapter, { + /// The base URL for the mock server base_url: Option, + /// The server configuration pub(crate) server_config: S::Config, } @@ -12,27 +15,56 @@ impl EasyHttpMockConfigBuilder where S: ServerAdapter, { + /// Sets the base URL for the mock server + /// + /// # Arguments + /// + /// * `base_url` - The base URL for the mock server + /// + /// # Returns + /// + /// * `Self` - The current instance + /// pub fn base_url(mut self, base_url: Option) -> Self { self.base_url = base_url; self } + /// Sets the server configuration + /// + /// # Arguments + /// + /// * `server_config` - The server configuration + /// + /// # Returns + /// + /// * `Self` - The current instance + /// pub fn server_config(mut self, server_config: S::Config) -> Self { self.server_config = server_config; self } + /// Builds the configuration + /// + /// # Returns + /// + /// * `EasyHttpMockConfig` - The built configuration + /// pub fn build(self) -> EasyHttpMockConfig { EasyHttpMockConfig { base_url: self.base_url, server_config: self.server_config } } } +/// EasyHttpMock server adapter configuration #[derive(Clone)] pub struct EasyHttpMockConfig where S: ServerAdapter, { + /// The base URL for the mock server pub(crate) base_url: Option, + /// The server configuration pub(crate) server_config: S::Config, } @@ -51,14 +83,32 @@ where S: ServerAdapter, S::Config: Clone + Default, { + /// Returns a new builder for the configuration + /// + /// # Returns + /// + /// * `EasyHttpMockConfigBuilder` - A new builder for the configuration + /// pub fn builder() -> EasyHttpMockConfigBuilder { EasyHttpMockConfigBuilder { base_url: None, server_config: S::Config::default() } } + /// Returns the base URL for the mock server + /// + /// # Returns + /// + /// * `&Option` - The base URL for the mock server + /// pub fn base_url(&self) -> &Option { &self.base_url } + /// Returns the server configuration + /// + /// # Returns + /// + /// * `&S::Config` - The server configuration + /// pub fn server_config(&self) -> &S::Config { &self.server_config } diff --git a/easyhttpmock/src/errors.rs b/easyhttpmock/src/errors.rs new file mode 100644 index 0000000..ea21cb8 --- /dev/null +++ b/easyhttpmock/src/errors.rs @@ -0,0 +1,66 @@ +use thiserror::Error; + +/// EasyHttpMock error types +#[derive(Debug, Error)] +pub enum EasyHttpMockError { + /// Server error + #[error("Server error: {0}")] + Server(#[from] ServerError), + /// Mock error + #[error("Mock error: {0}")] + Mock(#[from] MockError), +} + +/// Server related errors +#[derive(Debug, Clone, Error, PartialEq)] +pub enum ServerError { + /// Server config error + #[error("Server config error: {0}")] + Config(String), + /// Server start error + #[error("Server start error: {0}")] + Start(String), + /// Server stop error + #[error("Server stop error: {0}")] + Stop(String), + /// Server creation error + #[error("Server creation error: {0}")] + Creation(String), +} + +/// Mock related errors +#[derive(Debug, Clone, Error, PartialEq)] +pub enum MockError { + /// Mock not found + #[error("Mock not found")] + Notfound, + #[error("Mock already exists")] + /// Mock already exists + AlreadyExists, + /// Request error + #[error("Request error: {0}")] + Request(#[from] RequestError), +} + +/// Request related errors +#[derive(Debug, Clone, Error, PartialEq)] +pub enum RequestError { + /// Request failed + #[error("Request failed: {0}")] + Failed(String), + /// Invalid path + #[error("Invalid path, expected {0}")] + InvalidPath(String), + /// Invalid method + #[error("Invalid method, expected {0}")] + InvalidMethod(String), + /// Invalid query param + #[error("Invalid query param, expected {0}")] + InvalidQuery(String), + /// Invalid body + #[error("Invalid body, expected {0}")] + InvalidBody(String), + /// Invalid header + #[error("Invalid header, expected {0}")] + InvalidHeader(String), +} diff --git a/easyhttpmock/src/lib.rs b/easyhttpmock/src/lib.rs new file mode 100644 index 0000000..be0bd4f --- /dev/null +++ b/easyhttpmock/src/lib.rs @@ -0,0 +1,128 @@ +#![doc = include_str!("../README.md")] +#![deny(missing_docs)] +use crate::{ + config::EasyHttpMockConfig, errors::EasyHttpMockError, mock::Mock, server::ServerAdapter, +}; + +/// Configuration module +pub mod config; +/// Error module +pub mod errors; +/// Mock module +pub mod mock; +/// Server module +pub mod server; + +#[cfg(test)] +mod tests; + +/// Create a mock using a specific server implementation +pub struct EasyHttpMock +where + S: ServerAdapter, +{ + /// Configuration for the mock server + config: EasyHttpMockConfig, + /// The actual server implementation + server: S, +} + +impl EasyHttpMock { + /// Creates a new mock with the given configuration + /// + /// # Arguments + /// + /// * `config` - The configuration for the mock server + /// + /// # Returns + /// + /// * `Result, EasyHttpMockError>` - A result indicating whether the mock was created successfully + /// + pub fn new(config: EasyHttpMockConfig) -> Result, EasyHttpMockError> { + let server = S::new( + config + .server_config + .clone(), + )?; + + Ok(EasyHttpMock { config, server }) + } + + /// Returns the full URL for a given path + /// + /// # Arguments + /// + /// * `path` - The path to append to the base URL + /// + /// # Returns + /// + /// * `String` - The full URL for the given path + pub fn url(&self, path: &str) -> String { + if let Some(base_url) = &self.config.base_url { + format!("{}{}", base_url, path) + } else { + format!( + "{}{}", + self.server + .base_url(), + path + ) + } + } + + /// Returns the base URL for the mock server + /// + /// # Returns + /// + /// * `String` - The base URL for the mock server + /// + pub fn base_url(&self) -> String { + self.server + .base_url() + } + + /// Starts the mock server with the given mocker function + /// + /// # Arguments + /// + /// * `mocker` - A function that returns a `Mock` or an error + /// + /// # Returns + /// + /// * `Result<(), EasyHttpMockError>` - A result indicating whether the mock server started successfully + /// + /// # Examples + /// + /// ```rust,ignore + /// let mut mock = EasyHttpMock::new(EasyHttpMockConfig::builder().build()); + /// mock.mock(|| async { + /// Ok(Mock::of(Request::get("/test").build()).respond().with_status(200).build()) + /// }).await?; + /// ``` + pub fn register_mock(&mut self, mock: Mock) { + self.server + .register_mock(mock); + } + + /// Start server + /// + /// # Returns + /// + /// * `Result<(), EasyHttpMockError>` - A result indicating whether the server started successfully + pub async fn start(&mut self) -> Result<(), EasyHttpMockError> { + self.server + .start() + .await + } + + /// Assert that the server has stopped + /// + /// # Returns + /// + /// * `Result<(), EasyHttpMockError>` - A result indicating whether the server stopped successfully + pub async fn assert(&mut self) -> Result<(), EasyHttpMockError> { + self.server + .stop() + .await + } +} diff --git a/easyhttpmock/src/mock.rs b/easyhttpmock/src/mock.rs new file mode 100644 index 0000000..c987fd7 --- /dev/null +++ b/easyhttpmock/src/mock.rs @@ -0,0 +1,422 @@ +use std::collections::HashMap; + +use bytes::Bytes; +use http::{HeaderMap, Method, StatusCode}; + +/// Mock struct +pub struct Mock { + request: Request, + count: u32, + match_request: Option, +} + +impl Mock { + #[inline] + /// Create a new mock + pub fn of(request: Request) -> Self { + Self { request, count: 0, match_request: None } + } + + #[inline] + /// Report a call to this mock + pub fn report_call(&mut self) { + self.count += 1; + } + + #[inline] + /// Get the request that matched this mock + pub fn request(&self) -> &Request { + &self.request + } + + #[inline] + /// Match this mock with an actual request + pub fn match_with(&mut self, request: ActualRequest) { + self.match_request = Some(request); + } +} + +/// Extension trait for StatusCode to create responses +pub trait StatusCodeExt { + /// Create a response builder with this status code + fn respond(self) -> RespondBuilder; +} + +impl StatusCodeExt for StatusCode { + /// Create a response builder with this status code + fn respond(self) -> RespondBuilder { + RespondBuilder { status_code: self, headers: HashMap::new() } + } +} + +/// Extension trait for HTTP methods to create requests. +/// Allows creating requests using method names as strings or Method enum values. +/// +/// # Examples +/// ``` compile_fail +/// use http::Method; +/// use deboa::request::MethodExt; +/// +/// // Using Method enum +/// let request = Method::GET.has(); +/// +/// // Using string +/// let request = "GET".has(); +/// ``` +pub trait MethodExt { + /// Create a request builder with this method + fn has(self) -> RequestBuilder; +} + +impl MethodExt for Method { + #[inline] + /// Create a request builder with this method + fn has(self) -> RequestBuilder { + match self { + Method::GET => Request::builder(self), + Method::POST => Request::builder(self), + Method::PUT => Request::builder(self), + Method::DELETE => Request::builder(self), + Method::PATCH => Request::builder(self), + Method::HEAD => Request::builder(self), + Method::OPTIONS => Request::builder(self), + _ => panic!("Method not supported"), + } + } +} + +impl MethodExt for &str { + #[inline] + /// Create a request builder with this method + fn has(self) -> RequestBuilder { + match self { + "GET" | "get" => Request::builder(Method::GET), + "POST" | "post" => Request::builder(Method::POST), + "PUT" | "put" => Request::builder(Method::PUT), + "DELETE" | "delete" => Request::builder(Method::DELETE), + "PATCH" | "patch" => Request::builder(Method::PATCH), + "HEAD" | "head" => Request::builder(Method::HEAD), + "OPTIONS" | "options" => Request::builder(Method::OPTIONS), + _ => panic!("Method not supported"), + } + } +} + +/// Builder for creating actual HTTP requests to be matched against mocks +pub struct ActualRequestBuilder { + path: String, + method: Method, + headers: HeaderMap, + query_params: HashMap, + body: String, +} + +impl ActualRequestBuilder { + #[inline] + /// Set the path for this request + pub fn path(mut self, path: &str) -> Self { + self.path = path.to_string(); + self + } + + #[inline] + /// Set the method for this request + pub fn method(mut self, method: Method) -> Self { + self.method = method; + self + } + + #[inline] + /// Set the headers for this request + pub fn headers(mut self, headers: HeaderMap) -> Self { + self.headers = headers; + self + } + + #[inline] + /// Set the query parameters for this request + pub fn query_params(mut self, query_params: HashMap) -> Self { + self.query_params = query_params; + self + } + + #[inline] + /// Set the body for this request + pub fn body(mut self, body: &str) -> Self { + self.body = body.to_string(); + self + } + + #[inline] + /// Build the actual request + pub fn build(self) -> ActualRequest { + ActualRequest { + path: self.path, + method: self.method, + headers: self.headers, + query_params: self.query_params, + body: self.body, + } + } +} + +/// Represents an actual HTTP request that will be matched against mocks +pub struct ActualRequest { + path: String, + method: Method, + headers: HeaderMap, + query_params: HashMap, + body: String, +} + +impl ActualRequest { + #[inline] + /// Create a new actual request builder + pub fn builder() -> ActualRequestBuilder { + ActualRequestBuilder { + path: String::new(), + method: Method::GET, + headers: HeaderMap::new(), + query_params: HashMap::new(), + body: String::new(), + } + } + + #[inline] + /// Get the path + pub fn path(&self) -> &str { + &self.path + } + + #[inline] + /// Get the method + pub fn method(&self) -> &Method { + &self.method + } + + #[inline] + /// Get the headers + pub fn headers(&self) -> &HeaderMap { + &self.headers + } + + #[inline] + /// Get the query params + pub fn query_params(&self) -> &HashMap { + &self.query_params + } + + #[inline] + /// Get the body + pub fn body(&self) -> &str { + &self.body + } +} + +/// Builder for creating mock requests +pub struct RequestBuilder { + path: String, + method: Method, + headers: HeaderMap, + query_params: HashMap, + body: Bytes, +} + +impl RequestBuilder { + #[inline] + /// Set the path for this request + pub fn path(mut self, path: &str) -> Self { + self.path = path.to_string(); + self + } + + #[inline] + /// Set the method for this request + pub fn method(mut self, method: Method) -> Self { + self.method = method; + self + } + + #[inline] + /// Set the headers for this request + pub fn headers(mut self, headers: HeaderMap) -> Self { + self.headers = headers; + self + } + + #[inline] + /// Set the query parameters for this request + pub fn query_params(mut self, query_params: HashMap) -> Self { + self.query_params = query_params; + self + } + + #[inline] + /// Set the body for this request + pub fn body(mut self, body: &[u8]) -> Self { + self.body = Bytes::from(body.to_vec()); + self + } + + #[inline] + /// Set the response for this request + pub fn will_return(self, respond: Respond) -> Request { + Request { + path: self.path, + method: self.method, + headers: self.headers, + query_params: self.query_params, + body: self.body, + respond, + } + } +} + +/// Represents a mock HTTP request +pub struct Request { + path: String, + method: Method, + headers: HeaderMap, + query_params: HashMap, + body: Bytes, + respond: Respond, +} + +impl Request { + #[inline] + /// Create a new request builder + pub fn builder(method: Method) -> RequestBuilder { + RequestBuilder { + path: String::new(), + method, + headers: HeaderMap::new(), + query_params: HashMap::new(), + body: Bytes::new(), + } + } + + #[inline] + /// Get the path + pub fn path(&self) -> &str { + &self.path + } + + #[inline] + /// Get the method + pub fn method(&self) -> &Method { + &self.method + } + + #[inline] + /// Get the query params + pub fn query_params(&self) -> &HashMap { + &self.query_params + } + + #[inline] + /// Get the body + pub fn body(&self) -> &Bytes { + &self.body + } + + #[inline] + /// Get the headers + pub fn headers(&self) -> &HeaderMap { + &self.headers + } + + #[inline] + /// Get the respond + pub fn respond(&self) -> &Respond { + &self.respond + } +} + +/// Builder for what represents a response for a request +pub struct RespondBuilder { + status_code: StatusCode, + headers: HashMap, +} + +impl RespondBuilder { + #[inline] + /// Set the status code for this response + pub fn with_status(mut self, status: StatusCode) -> Self { + self.status_code = status; + self + } + + #[inline] + /// Set a header for this response + pub fn with_header(mut self, key: &str, value: &str) -> Self { + self.headers + .insert(key.to_string(), value.to_string()); + self + } + + #[inline] + /// Set multiple headers for this response + pub fn with_headers(mut self, entries: &[(&str, &str)]) -> Self { + for (key, value) in entries { + self.headers + .insert(key.to_string(), value.to_string()); + } + self + } + + #[inline] + /// Create an empty response + pub fn empty(self) -> Respond { + self.no_body() + } + + #[inline] + /// Create a response with no body + pub fn no_body(self) -> Respond { + Respond { status_code: self.status_code, headers: self.headers, body: Bytes::new() } + } + + #[inline] + /// Create a response with a body + pub fn with_body(self, body: &[u8]) -> Respond { + Respond { + status_code: self.status_code, + headers: self.headers, + body: Bytes::from(body.to_vec()), + } + } +} + +/// Represents how to respond for a request +pub struct Respond { + status_code: StatusCode, + headers: HashMap, + body: Bytes, +} + +impl Respond { + /// Initialize respond builder + #[inline] + /// Initialize respond builder + pub fn builder() -> RespondBuilder { + RespondBuilder { status_code: StatusCode::OK, headers: HashMap::new() } + } + + #[inline] + /// Get the status code + pub fn status_code(&self) -> StatusCode { + self.status_code + } + + #[inline] + /// Get the headers + pub fn headers(&self) -> HashMap { + self.headers.clone() + } + + #[inline] + /// Get the body + pub fn body(&self) -> Bytes { + self.body.clone() + } +} diff --git a/easyhttpmock/src/server.rs b/easyhttpmock/src/server.rs new file mode 100644 index 0000000..781e357 --- /dev/null +++ b/easyhttpmock/src/server.rs @@ -0,0 +1,88 @@ +use std::future::Future; + +use crate::{errors::EasyHttpMockError, mock::Mock}; + +/// Server adapter trait to allow different http server implementations +pub trait ServerAdapter { + /// The configuration for the server adapter + type Config: Clone; + + /// Create a new server adapter + /// + /// # Arguments + /// + /// * `config` - The configuration for the server adapter + /// + /// # Returns + /// + /// * `Result` - The server adapter or an error + fn new(config: Self::Config) -> Result + where + Self: Sized; + + /// Get the hostname of the server + /// + /// # Returns + /// + /// * `String` - The hostname of the server + fn hostname(&self) -> String; + + /// Get the base URL of the server + /// + /// # Returns + /// + /// * `String` - The base URL of the server + /// + fn base_url(&self) -> String; + + /// Get the configuration of the server + /// + /// # Returns + /// + /// * `&Self::Config` - The configuration of the server + /// + fn config(&self) -> &Self::Config; + + /// Set the mocker to handle incoming requests + /// + /// # Arguments + /// + /// * `mocker` - The mocker to handle incoming requests + /// + /// # Returns + /// + /// * `Result<(), EasyHttpMockError>` - The result of the operation + /// + fn register_mock(&mut self, mock: Mock); + + /// Start the server + /// + /// # Returns + /// + /// * `Result<(), EasyHttpMockError>` - The result of the operation + /// + fn start(&mut self) -> impl Future>; + + /// Stop the server + /// + /// # Returns + /// + /// * `Result<(), EasyHttpMockError>` - The result of the operation + /// + fn stop(&mut self) -> impl Future>; +} + +/// Port generator trait to allow different port generation strategies +pub trait PortGenerator +where + S: ServerAdapter, + S::Config: Clone, +{ + /// Generate a random port + fn random_port() -> u16 { + rand::random_range(9000..65535) + } + + /// Set the server to use a random port + fn with_random_port(self) -> Self; +} diff --git a/easyhttpmock/src/tests/config.rs b/easyhttpmock/src/tests/config.rs new file mode 100644 index 0000000..665d74e --- /dev/null +++ b/easyhttpmock/src/tests/config.rs @@ -0,0 +1,31 @@ +use crate::{ + config::EasyHttpMockConfig, + tests::server::{TestServer, TestServerConfig}, +}; + +#[test] +fn test_config() { + let config = EasyHttpMockConfig::::builder() + .base_url(Some("http://127.0.0.1:8080".to_string())) + .server_config(TestServerConfig::default()) + .build(); + + assert_eq!( + config + .server_config() + .port(), + 8080 + ); + assert_eq!( + config + .server_config() + .interface(), + "127.0.0.1" + ); + + assert_eq!( + config.base_url(), + &Some("http://127.0.0.1:8080".to_string()), + "base url should be http://127.0.0.1:8080" + ); +} diff --git a/easyhttpmock/src/tests/mock.rs b/easyhttpmock/src/tests/mock.rs new file mode 100644 index 0000000..7885970 --- /dev/null +++ b/easyhttpmock/src/tests/mock.rs @@ -0,0 +1,34 @@ +use std::error::Error; + +use googletest::{expect_that, gtest, prelude::eq}; +use http::{Method, StatusCode}; + +use crate::mock::{MethodExt, Mock, StatusCodeExt}; + +#[gtest] +fn test_mock_request() -> Result<(), Box> { + let mock = Mock::of( + Method::GET + .has() + .path("/test") + .will_return( + StatusCode::OK + .respond() + .with_body(b"teste"), + ), + ); + + expect_that!( + mock.request() + .method(), + eq(Method::GET) + ); + + expect_that!( + mock.request() + .path(), + eq("/test") + ); + + Ok(()) +} diff --git a/src/tests/mod.rs b/easyhttpmock/src/tests/mod.rs similarity index 70% rename from src/tests/mod.rs rename to easyhttpmock/src/tests/mod.rs index 21be1b5..cd5c040 100644 --- a/src/tests/mod.rs +++ b/easyhttpmock/src/tests/mod.rs @@ -1,2 +1,3 @@ mod config; +mod mock; mod server; diff --git a/easyhttpmock/src/tests/server.rs b/easyhttpmock/src/tests/server.rs new file mode 100644 index 0000000..72007de --- /dev/null +++ b/easyhttpmock/src/tests/server.rs @@ -0,0 +1,81 @@ +use std::error::Error; + +use crate::{errors::EasyHttpMockError, mock::Mock, server::ServerAdapter, EasyHttpMock}; + +#[derive(Debug, Clone)] +pub struct TestServerConfig { + port: u32, + interface: String, +} + +impl Default for TestServerConfig { + fn default() -> Self { + Self { port: 8080, interface: "127.0.0.1".to_string() } + } +} + +impl TestServerConfig { + pub fn port(&self) -> u32 { + self.port + } + + pub fn interface(&self) -> &str { + &self.interface + } +} + +pub struct TestServer { + config: TestServerConfig, + mock: Option, +} + +impl ServerAdapter for TestServer { + type Config = TestServerConfig; + + fn new(config: Self::Config) -> Result { + Ok(Self { config, mock: None }) + } + + fn hostname(&self) -> String { + "localhost".to_string() + } + + fn base_url(&self) -> String { + format!("http://{}:{}", self.hostname(), self.config.port) + } + + fn config(&self) -> &Self::Config { + &self.config + } + + fn register_mock(&mut self, mock: Mock) { + self.mock = Some(mock); + } + + async fn start(&mut self) -> Result<(), EasyHttpMockError> { + todo!() + } + + async fn stop(&mut self) -> Result<(), EasyHttpMockError> { + todo!() + } +} + +#[test] +fn test_server() -> Result<(), Box> { + let mock_server = EasyHttpMock::::new(crate::config::EasyHttpMockConfig { + server_config: TestServerConfig { port: 8080, interface: "127.0.0.1".to_string() }, + base_url: Some("http://127.0.0.1:8080".to_string()), + })?; + + assert_eq!( + mock_server + .config + .server_config + .port, + 8080, + "server port should be 8080" + ); + + Ok(()) +} diff --git a/samoyed.toml b/samoyed.toml deleted file mode 100644 index e889539..0000000 --- a/samoyed.toml +++ /dev/null @@ -1,3 +0,0 @@ -[hooks] -pre-push = "cargo test --release" -pre-commit = "cargo fmt --check && cargo clippy -- -D warnings" diff --git a/src/errors.rs b/src/errors.rs deleted file mode 100644 index 662d4f6..0000000 --- a/src/errors.rs +++ /dev/null @@ -1,17 +0,0 @@ -use thiserror::Error; - -#[derive(Debug, Error)] -pub enum EasyHttpMockError { - #[error("Server error: {0}")] - Server(#[from] ServerError), -} - -#[derive(Debug, Clone, Error, PartialEq)] -pub enum ServerError { - #[error("Server start error: {0}")] - Start(String), - #[error("Server stop error: {0}")] - Stop(String), - #[error("Server creation error: {0}")] - Creation(String), -} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 331e078..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,74 +0,0 @@ -use std::future::Future; - -use crate::{config::EasyHttpMockConfig, errors::EasyHttpMockError, server::ServerAdapter}; - -use bytes::Bytes; -use http::StatusCode; -use http_body_util::{Either, Full}; -use vetis::{errors::VetisError, Request, Response}; - -pub mod config; -pub mod errors; -pub mod server; - -mod tests; - -pub struct EasyHttpMock -where - S: ServerAdapter, -{ - config: EasyHttpMockConfig, - server: S, -} - -impl EasyHttpMock { - pub fn new(config: EasyHttpMockConfig) -> Result, EasyHttpMockError> { - let server = S::new( - config - .server_config - .clone(), - )?; - - Ok(EasyHttpMock { config, server }) - } - - pub fn url(&self, path: &str) -> String { - if let Some(base_url) = &self.config.base_url { - format!("{}{}", base_url, path) - } else { - format!( - "{}{}", - self.server - .base_url(), - path - ) - } - } - - pub fn base_url(&self) -> String { - self.server - .base_url() - } - - pub async fn start(&mut self, handler: H) -> Result<(), EasyHttpMockError> - where - H: Fn(Request) -> Fut + Send + Sync + 'static, - Fut: Future> + Send + Sync + 'static, - { - self.server - .start(handler) - .await - } - - pub async fn stop(&mut self) -> Result<(), EasyHttpMockError> { - self.server - .stop() - .await - } -} - -pub fn mock_response(status: StatusCode, body: &str) -> Response { - Response::builder() - .status(status) - .text(body) -} diff --git a/src/server/adapters/mod.rs b/src/server/adapters/mod.rs deleted file mode 100644 index 5308612..0000000 --- a/src/server/adapters/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod vetis_adapter; diff --git a/src/server/adapters/vetis_adapter.rs b/src/server/adapters/vetis_adapter.rs deleted file mode 100644 index d317ad9..0000000 --- a/src/server/adapters/vetis_adapter.rs +++ /dev/null @@ -1,250 +0,0 @@ -use std::future::Future; - -use vetis::{ - config::{ListenerConfig, SecurityConfig, ServerConfig, VirtualHostConfig}, - errors::VetisError, - server::{ - path::HandlerPath, - virtual_host::{handler_fn, VirtualHost}, - }, - Request, Response, Vetis, -}; - -use crate::{ - config::EasyHttpMockConfig, - errors::{EasyHttpMockError, ServerError}, - server::{PortGenerator, ServerAdapter}, - EasyHttpMock, -}; - -pub struct VetisAdapterConfigBuilder { - interface: String, - port: u16, - cert: Option>, - key: Option>, - ca: Option>, -} - -impl VetisAdapterConfigBuilder { - pub fn interface(mut self, interface: &str) -> Self { - self.interface = interface.to_string(); - self - } - - pub fn port(mut self, port: u16) -> Self { - self.port = port; - self - } - - pub fn cert(mut self, cert: Option>) -> Self { - self.cert = cert; - self - } - - pub fn key(mut self, key: Option>) -> Self { - self.key = key; - self - } - - pub fn ca(mut self, ca: Option>) -> Self { - self.ca = ca; - self - } - - pub fn build(self) -> VetisAdapterConfig { - VetisAdapterConfig { - interface: self.interface, - port: self.port, - cert: self.cert, - key: self.key, - ca: self.ca, - } - } -} - -#[derive(Clone)] -pub struct VetisAdapterConfig { - interface: String, - port: u16, - cert: Option>, - key: Option>, - ca: Option>, -} - -impl Default for VetisAdapterConfig { - fn default() -> Self { - Self { interface: "0.0.0.0".into(), port: 80, cert: None, key: None, ca: None } - } -} - -impl VetisAdapterConfig { - pub fn builder() -> VetisAdapterConfigBuilder { - VetisAdapterConfigBuilder { - interface: "0.0.0.0".into(), - port: 80, - cert: None, - key: None, - ca: None, - } - } - - pub fn interface(&self) -> &str { - &self.interface - } - - pub fn port(&self) -> u16 { - self.port - } - - pub fn cert(&self) -> &Option> { - &self.cert - } - - pub fn key(&self) -> &Option> { - &self.key - } - - pub fn ca(&self) -> &Option> { - &self.ca - } -} - -impl From for ServerConfig { - fn from(config: VetisAdapterConfig) -> Self { - let listener_config = ListenerConfig::builder() - .interface(&config.interface) - .port(config.port) - .build(); - ServerConfig::builder() - .add_listener(listener_config) - .build() - } -} - -pub struct VetisAdapter { - server: Vetis, - config: VetisAdapterConfig, -} - -impl PortGenerator for VetisAdapterConfigBuilder { - fn with_random_port(self) -> Self { - let port = rand::random_range(9000..65535); - self.port(port) - } -} - -impl Default for EasyHttpMockConfig { - fn default() -> Self { - let server_config = VetisAdapterConfig::builder() - .interface("0.0.0.0") - .cert(None) - .key(None) - .ca(None) - .port(80) - .build(); - EasyHttpMockConfig::::builder() - .server_config(server_config.clone()) - .base_url(format!("http://localhost:{}", 80).into()) - .build() - } -} - -impl Default for EasyHttpMock { - fn default() -> Self { - EasyHttpMock::new(EasyHttpMockConfig::default()).unwrap() - } -} - -impl ServerAdapter for VetisAdapter { - type Config = VetisAdapterConfig; - - fn new(config: Self::Config) -> Result { - let vetis_config = config - .clone() - .into(); - - let server = Vetis::new(vetis_config); - - Ok(Self { server, config }) - } - - fn base_url(&self) -> String { - if self - .config - .cert - .is_some() - { - format!("https://localhost:{}", self.config.port()) - } else { - format!("http://localhost:{}", self.config.port()) - } - } - - fn config(&self) -> &Self::Config { - &self.config - } - - async fn start(&mut self, handler: H) -> Result<(), EasyHttpMockError> - where - H: Fn(Request) -> Fut + Send + Sync + 'static, - Fut: Future> + Send + Sync + 'static, - { - let path = HandlerPath::builder() - .uri("/") - .handler(handler_fn(handler)) - .build() - .unwrap(); - - let host_config = VirtualHostConfig::builder() - .hostname("localhost") - .port(self.config.port()); - - let host_config = if let Some(((cert, key), ca)) = self - .config - .cert - .as_ref() - .zip( - self.config - .key - .as_ref(), - ) - .zip( - self.config - .ca - .as_ref(), - ) { - host_config.security( - SecurityConfig::builder() - .cert_from_bytes(cert.clone()) - .key_from_bytes(key.clone()) - .ca_cert_from_bytes(ca.clone()) - .build(), - ) - } else { - host_config - }; - - let host_config = host_config - .build() - .map_err(|e| EasyHttpMockError::Server(ServerError::Creation(e.to_string())))?; - - let mut host = VirtualHost::new(host_config); - host.add_path(path); - - self.server - .add_virtual_host(host) - .await; - - self.server - .start() - .await - .map_err(|e| EasyHttpMockError::Server(ServerError::Start(e.to_string()))) - } - - async fn stop(&mut self) -> Result<(), EasyHttpMockError> { - self.server - .stop() - .await - .map_err(|e| EasyHttpMockError::Server(ServerError::Stop(e.to_string()))) - } -} diff --git a/src/server/mod.rs b/src/server/mod.rs deleted file mode 100644 index 3250ba9..0000000 --- a/src/server/mod.rs +++ /dev/null @@ -1,38 +0,0 @@ -use std::future::Future; - -use vetis::{errors::VetisError, Request, Response}; - -use crate::errors::EasyHttpMockError; - -pub mod adapters; - -pub trait ServerAdapter { - type Config: Clone; - - fn new(config: Self::Config) -> Result - where - Self: Sized; - - fn base_url(&self) -> String; - - fn config(&self) -> &Self::Config; - - fn start(&mut self, handler: H) -> impl Future> - where - H: Fn(Request) -> Fut + Send + Sync + 'static, - Fut: Future> + Send + Sync + 'static; - - fn stop(&mut self) -> impl Future>; -} - -pub trait PortGenerator -where - S: ServerAdapter, - S::Config: Clone, -{ - fn random_port() -> u16 { - rand::random_range(9000..65535) - } - - fn with_random_port(self) -> Self; -} diff --git a/src/tests/config.rs b/src/tests/config.rs deleted file mode 100644 index 76021c7..0000000 --- a/src/tests/config.rs +++ /dev/null @@ -1,340 +0,0 @@ -#[cfg(test)] -mod easy_http_mock_config_tests { - use crate::{ - config::EasyHttpMockConfig, - server::adapters::vetis_adapter::{VetisAdapter, VetisAdapterConfig}, - }; - - #[test] - fn test_easy_http_mock_config_default() { - let config = EasyHttpMockConfig::::default(); - - assert!(config - .base_url() - .is_some()); - assert_eq!( - config - .base_url() - .as_ref() - .unwrap(), - "http://localhost:80" - ); - assert_eq!( - config - .server_config() - .port(), - 80 - ); - assert_eq!( - config - .server_config() - .interface(), - "0.0.0.0" - ); - } - - #[test] - fn test_easy_http_mock_config_builder_default() { - let config = EasyHttpMockConfig::::builder().build(); - - assert!(config - .base_url() - .is_none()); - assert_eq!( - config - .server_config() - .port(), - 80 - ); - assert_eq!( - config - .server_config() - .interface(), - "0.0.0.0" - ); - } - - #[test] - fn test_easy_http_mock_config_builder_with_base_url() { - let base_url = "https://api.example.com".to_string(); - let config = EasyHttpMockConfig::::builder() - .base_url(Some(base_url.clone())) - .build(); - - assert_eq!(config.base_url(), &Some(base_url)); - assert_eq!( - config - .server_config() - .port(), - 80 - ); - assert_eq!( - config - .server_config() - .interface(), - "0.0.0.0" - ); - } - - #[test] - fn test_easy_http_mock_config_builder_with_none_base_url() { - let config = EasyHttpMockConfig::::builder() - .base_url(None) - .build(); - - assert!(config - .base_url() - .is_none()); - assert_eq!( - config - .server_config() - .port(), - 80 - ); - } - - #[test] - fn test_easy_http_mock_config_builder_with_server_config() { - let server_config = VetisAdapterConfig::builder() - .interface("127.0.0.1") - .port(3000) - .build(); - - let config = EasyHttpMockConfig::::builder() - .server_config(server_config.clone()) - .build(); - - assert!(config - .base_url() - .is_none()); - assert_eq!( - config - .server_config() - .port(), - 3000 - ); - assert_eq!( - config - .server_config() - .interface(), - "127.0.0.1" - ); - } - - #[test] - fn test_easy_http_mock_config_builder_complete() { - let base_url = "https://test.mock".to_string(); - let server_config = VetisAdapterConfig::builder() - .interface("0.0.0.0") - .port(8443) - .build(); - - let config = EasyHttpMockConfig::::builder() - .base_url(Some(base_url.clone())) - .server_config(server_config.clone()) - .build(); - - assert_eq!(config.base_url(), &Some(base_url)); - assert_eq!( - config - .server_config() - .port(), - 8443 - ); - assert_eq!( - config - .server_config() - .interface(), - "0.0.0.0" - ); - } - - #[test] - fn test_easy_http_mock_config_builder_chaining() { - let server_config = VetisAdapterConfig::builder() - .interface("192.168.1.100") - .port(9090) - .build(); - - let config = EasyHttpMockConfig::::builder() - .base_url(Some("https://chained.mock".to_string())) - .server_config(server_config) - .build(); - - assert_eq!( - config - .base_url() - .as_ref() - .unwrap(), - "https://chained.mock" - ); - assert_eq!( - config - .server_config() - .port(), - 9090 - ); - assert_eq!( - config - .server_config() - .interface(), - "192.168.1.100" - ); - } - - #[test] - fn test_easy_http_mock_config_empty_base_url() { - let config = EasyHttpMockConfig::::builder() - .base_url(Some("".to_string())) - .build(); - - assert_eq!(config.base_url(), &Some("".to_string())); - } - - #[test] - fn test_easy_http_mock_config_long_base_url() { - let long_url = "https://very.long.domain.name.with.many.subdomains.for.testing.purposes.example.com:8443/api/v1".to_string(); - let config = EasyHttpMockConfig::::builder() - .base_url(Some(long_url.clone())) - .build(); - - assert_eq!(config.base_url(), &Some(long_url)); - } -} - -#[cfg(test)] -mod integration_tests { - use crate::{ - config::EasyHttpMockConfig, - server::adapters::vetis_adapter::{VetisAdapter, VetisAdapterConfig}, - }; - - #[test] - fn test_config_with_vetis_server_adapter_integration() { - let server_config = VetisAdapterConfig::builder() - .interface("0.0.0.0") - .port(443) - .build(); - - let mock_config = EasyHttpMockConfig::::builder() - .base_url(Some("https://secure.mock".to_string())) - .server_config(server_config) - .build(); - - assert_eq!( - mock_config - .server_config() - .port(), - 443 - ); - } - - #[test] - fn test_multiple_config_instances() { - let config1 = EasyHttpMockConfig::::builder() - .base_url(Some("https://config1.mock".to_string())) - .server_config( - VetisAdapterConfig::builder() - .interface("0.0.0.0") - .port(8080) - .build(), - ) - .build(); - - let config2 = EasyHttpMockConfig::::builder() - .base_url(Some("https://config2.mock".to_string())) - .server_config( - VetisAdapterConfig::builder() - .interface("0.0.0.0") - .port(9090) - .build(), - ) - .build(); - - assert_ne!(config1.base_url(), config2.base_url()); - assert_ne!( - config1 - .server_config() - .port(), - config2 - .server_config() - .port() - ); - } - - #[test] - fn test_config_immutability() { - let config = EasyHttpMockConfig::::builder() - .base_url(Some("https://immutable.mock".to_string())) - .server_config( - VetisAdapterConfig::builder() - .port(3000) - .build(), - ) - .build(); - - assert_eq!(config.base_url(), &Some("https://immutable.mock".to_string())); - assert_eq!( - config - .server_config() - .port(), - 3000 - ); - - let _new_config = EasyHttpMockConfig::::builder() - .base_url(Some("https://new.mock".to_string())) - .server_config( - VetisAdapterConfig::builder() - .port(3000) - .build(), - ) - .build(); - - assert_eq!(config.base_url(), &Some("https://immutable.mock".to_string())); - assert_eq!( - config - .server_config() - .port(), - 3000 - ); - } - - #[test] - fn test_config_with_different_server_configs() { - let http_config = VetisAdapterConfig::builder() - .interface("0.0.0.0") - .port(80) - .build(); - - let https_config = VetisAdapterConfig::builder() - .interface("0.0.0.0") - .port(443) - .build(); - - let http_mock = EasyHttpMockConfig::::builder() - .base_url(Some("http://http.mock".to_string())) - .server_config(http_config) - .build(); - - let https_mock = EasyHttpMockConfig::::builder() - .base_url(Some("https://https.mock".to_string())) - .server_config(https_config) - .build(); - - assert_eq!(http_mock.base_url(), &Some("http://http.mock".to_string())); - assert_eq!( - http_mock - .server_config() - .port(), - 80 - ); - - assert_eq!(https_mock.base_url(), &Some("https://https.mock".to_string())); - assert_eq!( - https_mock - .server_config() - .port(), - 443 - ); - } -} diff --git a/src/tests/server.rs b/src/tests/server.rs deleted file mode 100644 index 0f4d5bc..0000000 --- a/src/tests/server.rs +++ /dev/null @@ -1,384 +0,0 @@ -#[cfg(test)] -mod easy_http_mock_server_tests { - use crate::{ - config::EasyHttpMockConfig, - server::adapters::vetis_adapter::{VetisAdapter, VetisAdapterConfig}, - EasyHttpMock, - }; - use bytes::Bytes; - use http::StatusCode; - use http_body_util::{Either, Full}; - use std::time::Duration; - use vetis::{Request, Response}; - - #[tokio::test] - async fn test_easy_http_mock_default() { - let mock = EasyHttpMock::::default(); - - assert_eq!(mock.base_url(), "http://localhost:80"); - assert_eq!(mock.url("/test"), "http://localhost:80/test"); - assert_eq!(mock.url(""), "http://localhost:80"); - } - - #[tokio::test] - async fn test_easy_http_mock_with_custom_config() -> Result<(), Box> { - let config = EasyHttpMockConfig::::builder() - .base_url(Some("https://custom.mock".to_string())) - .server_config( - VetisAdapterConfig::builder() - .interface("127.0.0.1") - .port(3000) - .build(), - ) - .build(); - - let mock = EasyHttpMock::new(config)?; - - assert_eq!(mock.base_url(), "http://localhost:3000"); - assert_eq!(mock.url("/api"), "https://custom.mock/api"); - assert_eq!(mock.url(""), "https://custom.mock"); - - Ok(()) - } - - #[tokio::test] - async fn test_server_lifecycle() -> Result<(), Box> { - let config = EasyHttpMockConfig::::builder() - .server_config( - VetisAdapterConfig::builder() - .interface("127.0.0.1") - .port(4000) - .build(), - ) - .build(); - - let mut mock = EasyHttpMock::new(config)?; - - let handler = |_req: Request| async move { - let response: Response = Response::builder() - .status(StatusCode::OK) - .text("Hello, World!"); - Ok(response) - }; - - let result = mock - .start(handler) - .await; - if let Err(e) = &result { - println!("Server start error: {:?}", e); - } - assert!(result.is_ok()); - - tokio::time::sleep(Duration::from_millis(100)).await; - - let result = mock.stop().await; - assert!(result.is_ok()); - - Ok(()) - } - - #[tokio::test] - async fn test_url_generation() -> Result<(), Box> { - let mock = EasyHttpMock::::default(); - assert_eq!(mock.url("/api/users"), "http://localhost:80/api/users"); - assert_eq!(mock.url("/"), "http://localhost:80/"); - assert_eq!(mock.url(""), "http://localhost:80"); - assert_eq!(mock.url("test"), "http://localhost:80test"); - - let config = EasyHttpMockConfig::::builder() - .base_url(Some("https://api.example.com".to_string())) - .server_config( - VetisAdapterConfig::builder() - .interface("127.0.0.1") - .port(8181) - .build(), - ) - .build(); - - let mock = EasyHttpMock::new(config)?; - assert_eq!(mock.url("/v1/users"), "https://api.example.com/v1/users"); - assert_eq!(mock.url("/"), "https://api.example.com/"); - assert_eq!(mock.url(""), "https://api.example.com"); - assert_eq!(mock.url("health"), "https://api.example.comhealth"); - - Ok(()) - } - - #[tokio::test] - async fn test_server_with_different_responses() -> Result<(), Box> { - let config = EasyHttpMockConfig::::builder() - .server_config( - VetisAdapterConfig::builder() - .interface("127.0.0.1") - .port(9999) - .build(), - ) - .build(); - - let mut mock = EasyHttpMock::new(config)?; - - let handler = |req: Request| async move { - let path = req.uri().path(); - let (status, body) = match path { - "/health" => (StatusCode::OK, "OK"), - "/not-found" => (StatusCode::NOT_FOUND, "Not Found"), - _ => (StatusCode::OK, "Default Response"), - }; - - let response: Response = Response::builder() - .status(status) - .text(body); - Ok(response) - }; - - mock.start(handler) - .await - .unwrap(); - - tokio::time::sleep(Duration::from_millis(100)).await; - - assert!(mock - .base_url() - .contains("localhost:")); - assert!(!mock - .base_url() - .contains("localhost:80")); - - mock.stop().await?; - - Ok(()) - } - - #[tokio::test] - async fn test_server_with_json_response() -> Result<(), Box> { - let config = EasyHttpMockConfig::::builder() - .server_config( - VetisAdapterConfig::builder() - .interface("127.0.0.1") - .port(7777) // Use random available port - .build(), - ) - .build(); - - let mut mock = EasyHttpMock::new(config)?; - - let handler = |_req: Request| async move { - let json_body = r#"{ - "id": 1, - "name": "Test User", - "email": "test@example.com", - "active": true - }"#; - - let response: Response = Response::builder() - .status(StatusCode::OK) - .header( - "Content-Type", - "application/json" - .parse() - .unwrap(), - ) - .text(json_body); - Ok(response) - }; - - mock.start(handler) - .await - .unwrap(); - - tokio::time::sleep(Duration::from_millis(100)).await; - tokio::time::sleep(Duration::from_millis(100)).await; - - assert!(mock - .base_url() - .contains("localhost:7777")); - assert!(!mock - .base_url() - .is_empty()); - - mock.stop() - .await - .unwrap(); - - Ok(()) - } -} - -#[cfg(test)] -mod integration_tests { - use crate::{ - config::EasyHttpMockConfig, - server::adapters::vetis_adapter::{VetisAdapter, VetisAdapterConfig}, - EasyHttpMock, - }; - use http::StatusCode; - use std::time::Duration; - use vetis::{Request, Response}; - - #[tokio::test] - async fn test_multiple_server_instances() -> Result<(), Box> { - let config1 = EasyHttpMockConfig::::builder() - .server_config( - VetisAdapterConfig::builder() - .interface("127.0.0.1") - .port(8081) - .build(), - ) - .build(); - - let config2 = EasyHttpMockConfig::::builder() - .server_config( - VetisAdapterConfig::builder() - .interface("127.0.0.1") - .port(8082) - .build(), - ) - .build(); - - let mut mock1 = EasyHttpMock::new(config1)?; - let mut mock2 = EasyHttpMock::new(config2)?; - - let handler1 = |_req: Request| async move { - let response: Response = Response::builder() - .status(StatusCode::OK) - .text("Server 1 response"); - Ok(response) - }; - - let handler2 = |_req: Request| async move { - let response: Response = Response::builder() - .status(StatusCode::OK) - .text("Server 2 response"); - Ok(response) - }; - - mock1 - .start(handler1) - .await - .unwrap(); - mock2 - .start(handler2) - .await - .unwrap(); - - tokio::time::sleep(Duration::from_millis(100)).await; - - let base_url1 = mock1.base_url(); - let base_url2 = mock2.base_url(); - - assert!(base_url1.contains("localhost")); - assert!(base_url2.contains("localhost")); - - mock1 - .stop() - .await - .unwrap(); - mock2 - .stop() - .await - .unwrap(); - - Ok(()) - } - - #[tokio::test] - async fn test_server_with_random_port() -> Result<(), Box> { - // Create a mock server with random port - let config = EasyHttpMockConfig::::builder() - .server_config( - VetisAdapterConfig::builder() - .interface("127.0.0.1") - .port(8888) // Use random available port - .build(), - ) - .build(); - - let mut mock = EasyHttpMock::new(config)?; - - let handler = |_req: Request| async move { - let response: Response = Response::builder() - .status(StatusCode::OK) - .text("Random port test"); - Ok(response) - }; - - mock.start(handler) - .await - .unwrap(); - - tokio::time::sleep(Duration::from_millis(100)).await; - tokio::time::sleep(Duration::from_millis(100)).await; - - let base_url = mock.base_url(); - - assert!(base_url.contains("localhost:8888")); - assert!(!base_url.contains("localhost:80")); - - mock.stop() - .await - .unwrap(); - - Ok(()) - } - - #[tokio::test] - async fn test_server_restart() -> Result<(), Box> { - // Use a random available port to avoid permission issues - let config = EasyHttpMockConfig::::builder() - .server_config( - VetisAdapterConfig::builder() - .interface("127.0.0.1") - .port(5555) // Use random available port - .build(), - ) - .build(); - - let mut mock = EasyHttpMock::new(config)?; - - let handler = |_req: Request| async move { - let response: Response = Response::builder() - .status(StatusCode::OK) - .text("Restart test"); - Ok(response) - }; - - mock.start(handler) - .await - .unwrap(); - tokio::time::sleep(Duration::from_millis(100)).await; - - // Stop the server - mock.stop() - .await - .unwrap(); - tokio::time::sleep(Duration::from_millis(50)).await; - - // Start the server again with a new handler - let handler2 = |_req: Request| async move { - let response: Response = Response::builder() - .status(StatusCode::OK) - .text("Restarted"); - Ok(response) - }; - - mock.start(handler2) - .await - .unwrap(); - tokio::time::sleep(Duration::from_millis(100)).await; - - // Verify server is running again - assert!(mock - .base_url() - .contains("localhost:5555")); - assert!(!mock - .base_url() - .is_empty()); - - // Stop the server - mock.stop() - .await - .unwrap(); - - Ok(()) - } -}