diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aaf2b14..8114797 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,8 +47,6 @@ jobs: - uses: actions/setup-python@v7 with: python-version: "3.14" - - name: Install native build dependencies - run: sudo apt-get update && sudo apt-get install -y cmake clang libclang-dev - name: Install Rust tooling run: rustup toolchain install stable --profile minimal --component rustfmt --component clippy && rustup default stable - uses: actions/cache@v5 @@ -79,8 +77,6 @@ jobs: with: python-version: ${{ matrix.python }} allow-prereleases: ${{ matrix.python == '3.15' }} - - name: Install native build dependencies - run: sudo apt-get update && sudo apt-get install -y cmake clang libclang-dev - name: Install Rust run: rustup toolchain install stable --profile minimal && rustup default stable - uses: actions/cache@v5 @@ -108,8 +104,6 @@ jobs: - uses: actions/setup-python@v7 with: python-version: "3.14" - - name: Install native build dependencies - run: sudo apt-get update && sudo apt-get install -y cmake clang libclang-dev - name: Install coverage tooling run: | rustup toolchain install stable --profile minimal --component llvm-tools-preview diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..464e1f7 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,101 @@ +name: Release wheels + +on: + push: + tags: ["v*"] + pull_request: + paths: [".github/workflows/release.yml"] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: release-${{ github.ref }} + cancel-in-progress: false + +jobs: + wheels: + runs-on: ubuntu-24.04 + timeout-minutes: 60 + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-python@v7 + with: + python-version: "3.14" + - name: Validate release version + if: startsWith(github.ref, 'refs/tags/') + env: + RELEASE_TAG: ${{ github.ref_name }} + run: | + python - <<'PY' + import os + import tomllib + from pathlib import Path + project = tomllib.loads(Path("pyproject.toml").read_text())["project"]["version"] + crate = tomllib.loads(Path("Cargo.toml").read_text())["package"]["version"] + tag = os.environ["RELEASE_TAG"] + if tag != f"v{project}" or project != crate: + raise SystemExit(f"Version mismatch: tag={tag}, Python={project}, Rust={crate}") + PY + - name: Build portable Linux wheels + uses: PyO3/maturin-action@v1 + with: + target: x86_64 + manylinux: "2_28" + before-script: dnf install -y cmake + args: --release --locked --out dist --interpreter python3.10 python3.11 python3.12 python3.13 python3.14 python3.15 + - uses: actions/upload-artifact@v7 + with: + name: release-wheels + path: dist/*.whl + if-no-files-found: error + + smoke: + needs: wheels + runs-on: ubuntu-24.04 + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: + python: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15"] + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-python@v7 + with: + python-version: ${{ matrix.python }} + allow-prereleases: ${{ matrix.python == '3.15' }} + - uses: actions/download-artifact@v8 + with: + name: release-wheels + path: dist + - name: Install built wheel + run: | + python -m pip install --only-binary=:all: --no-index --no-deps --find-links dist proxyprobe + - name: Install runtime dependencies + run: python -m pip install "rsloop>=0.1.56" + - name: Test installed wheel + run: python -m unittest discover -s tests -v + + release: + if: startsWith(github.ref, 'refs/tags/') + needs: [wheels, smoke] + runs-on: ubuntu-24.04 + timeout-minutes: 10 + permissions: + contents: write + steps: + - uses: actions/download-artifact@v8 + with: + name: release-wheels + path: dist + - name: Create release and upload wheels + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + RELEASE_TAG: ${{ github.ref_name }} + run: | + if ! gh release view "$RELEASE_TAG" >/dev/null 2>&1; then + gh release create "$RELEASE_TAG" --verify-tag --generate-notes + fi + gh release upload "$RELEASE_TAG" dist/*.whl --clobber diff --git a/Cargo.lock b/Cargo.lock index 9ddb0af..6739cec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,15 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr", -] - [[package]] name = "async-channel" version = "1.9.0" @@ -196,22 +187,10 @@ dependencies = [ ] [[package]] -name = "bindgen" -version = "0.72.1" +name = "base64" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" -dependencies = [ - "bitflags", - "cexpr", - "clang-sys", - "itertools", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn 2.0.117", -] +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" [[package]] name = "bitflags" @@ -232,31 +211,6 @@ dependencies = [ "piper", ] -[[package]] -name = "btls" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c5e60b8c8d282c86360cab651ded04ab0335a7b5390c8d34145cbeab8cacf5f" -dependencies = [ - "bitflags", - "btls-sys", - "foreign-types", - "libc", - "openssl-macros", -] - -[[package]] -name = "btls-sys" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b1b8638a2e1c38a5ae4efa90ae57e643baec35a30d03fc5b399b893adc4954b" -dependencies = [ - "bindgen", - "cmake", - "fs_extra", - "fslock", -] - [[package]] name = "bumpalo" version = "3.20.2" @@ -281,15 +235,6 @@ dependencies = [ "shlex", ] -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - [[package]] name = "cfg-if" version = "1.0.4" @@ -297,14 +242,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] -name = "clang-sys" -version = "1.8.1" +name = "cfg_aliases" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" + +[[package]] +name = "chacha20" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" dependencies = [ - "glob", - "libc", - "libloading", + "cfg-if", + "cpufeatures", + "rand_core", ] [[package]] @@ -316,6 +267,16 @@ dependencies = [ "cc", ] +[[package]] +name = "combine" +version = "4.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfc320937d09e6de266b31b9afb480f197d7a861be86be7cb2ea7e5d1bfffc5e" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -341,6 +302,15 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cpufeatures" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" +dependencies = [ + "libc", +] + [[package]] name = "crossbeam-channel" version = "0.5.15" @@ -382,12 +352,6 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - [[package]] name = "equivalent" version = "1.0.2" @@ -449,39 +413,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foldhash" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" - -[[package]] -name = "foreign-types" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" -dependencies = [ - "foreign-types-macros", - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-macros" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "foreign-types-shared" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" - [[package]] name = "form_urlencoded" version = "1.2.2" @@ -497,16 +428,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" -[[package]] -name = "fslock" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "futures" version = "0.3.32" @@ -615,8 +536,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -627,15 +550,23 @@ checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", - "r-efi", + "r-efi 5.3.0", "wasip2", ] [[package]] -name = "glob" -version = "0.3.3" +name = "getrandom" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi 6.0.0", + "rand_core", + "wasm-bindgen", +] [[package]] name = "gloo-timers" @@ -650,19 +581,29 @@ dependencies = [ ] [[package]] -name = "hashbrown" -version = "0.16.1" +name = "h2" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +checksum = "7d29020232d6aa3fb1daca64c1127cf662cf97f254ae16c18c05b8ab635fc118" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] [[package]] name = "hashbrown" -version = "0.17.1" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" -dependencies = [ - "foldhash", -] +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "heck" @@ -710,29 +651,70 @@ dependencies = [ ] [[package]] -name = "http2" -version = "0.5.20" +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d3114be2f413b2e491e686b93a28cda30c355cffc8d091a57f8be4b1342896" +checksum = "2c3e324da4c95177d6291d4c8730197c0d1822f8a9766814a4a44fa5ab797c9c" dependencies = [ "atomic-waker", "bytes", - "fnv", + "futures-channel", "futures-core", - "futures-sink", + "h2", "http", - "indexmap", - "slab", + "http-body", + "httparse", + "itoa", + "pin-project-lite", "smallvec", "tokio", - "tokio-util", + "want", ] [[package]] -name = "httparse" -version = "1.10.1" +name = "hyper-rustls" +version = "0.27.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +checksum = "dfa8e654703247911e29c23fbeaa261834bd9bb74efba2f9acddc37bfb127f53" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc03d96684f9226b8a787cdb71488417b53ab5ea8fdb1dac946cb9431cc8bff" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] [[package]] name = "icu_collections" @@ -843,7 +825,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown", ] [[package]] @@ -864,19 +846,59 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" [[package]] -name = "itertools" -version = "0.13.0" +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jni" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" dependencies = [ - "either", + "cfg-if", + "combine", + "jni-macros", + "jni-sys", + "log", + "simd_cesu8", + "thiserror", + "walkdir", + "windows-link", ] [[package]] -name = "itoa" -version = "1.0.18" +name = "jni-macros" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.117", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.117", +] [[package]] name = "jobserver" @@ -915,16 +937,6 @@ version = "0.2.190" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce5d3ddc6d3fa000eb1536d85e147bfe31aacaba692ed6a876f95cb7c855be78" -[[package]] -name = "libloading" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" -dependencies = [ - "cfg-if", - "windows-link", -] - [[package]] name = "linux-raw-sys" version = "0.12.1" @@ -956,13 +968,10 @@ dependencies = [ ] [[package]] -name = "lru" -version = "0.18.5" +name = "lru-slab" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef9ac18847474e638e3702b76c65d4eb93428471a74778ef0f1be711717f89b5" -dependencies = [ - "hashbrown 0.17.1", -] +checksum = "4050469837a6ff301cd14c1f8f24f88549e6d548f24f64e2148eb0f72cebc51f" [[package]] name = "memchr" @@ -970,12 +979,6 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "mio" version = "1.2.0" @@ -988,16 +991,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - [[package]] name = "once_cell" version = "1.21.4" @@ -1010,17 +1003,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe21416a02c693fb9f980befcb230ecc70b0b3d1cc4abf88b9675c4c1457f0c" -[[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.2.1" @@ -1130,10 +1112,10 @@ dependencies = [ "async-std", "futures", "pyo3", + "reqwest", "rsloop", "tokio", "url", - "wreq", ] [[package]] @@ -1207,6 +1189,63 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "quinn" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4051e23e9185c255a7e33ef59cdbca87a22d359052eecd22fc6b901fb37d9d11" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e750cca55fe4f0439a15d0bb529da9651e79993e8e72c61a899a36d462befbe" +dependencies = [ + "aws-lc-rs", + "bytes", + "getrandom 0.4.3", + "lru-slab", + "rand", + "rand_pcg", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af66907df18639dcf4db56ca65490cabc4b27a97dbadd96f2926cca73298f016" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "quote" version = "1.0.45" @@ -1223,42 +1262,84 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] -name = "redox_syscall" -version = "0.5.18" +name = "r-efi" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c9fb96cbc91e3478eaae79a69fcd3f1ae4ad052e471fe6732fff548984b4af" dependencies = [ - "bitflags", + "chacha20", + "getrandom 0.4.3", + "rand_core", ] [[package]] -name = "regex" -version = "1.12.3" +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "rand_pcg" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", + "rand_core", ] [[package]] -name = "regex-automata" -version = "0.4.14" +name = "redox_syscall" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", + "bitflags", ] [[package]] -name = "regex-syntax" -version = "0.8.10" +name = "reqwest" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +checksum = "16a1cfa75cc186dd73d5818e510e042e40927bccc9c236b061cea97e1eb08029" +dependencies = [ + "base64", + "bytes", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] [[package]] name = "ring" @@ -1307,9 +1388,18 @@ dependencies = [ [[package]] name = "rustc-hash" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] [[package]] name = "rustix" @@ -1366,9 +1456,37 @@ version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" dependencies = [ + "web-time", "zeroize", ] +[[package]] +name = "rustls-platform-verifier" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1167586491e2b18b8bfbb293e8180ec17c201c4f076d7cb3070ca964e7598f98" +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.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eec689c0bc40ff2458a5977b6619cb718087084a18e02a131c599b62d05e1a5f" + [[package]] name = "rustls-webpki" version = "0.103.10" @@ -1387,6 +1505,15 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +[[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" @@ -1425,6 +1552,12 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + [[package]] name = "serde" version = "1.0.228" @@ -1480,6 +1613,22 @@ dependencies = [ "libc", ] +[[package]] +name = "simd_cesu8" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "slab" version = "0.4.12" @@ -1564,22 +1713,22 @@ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" [[package]] name = "thiserror" -version = "1.0.69" +version = "2.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.69" +version = "2.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.6", ] [[package]] @@ -1592,6 +1741,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinyvec" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" + [[package]] name = "tokio" version = "1.53.2" @@ -1609,16 +1764,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "tokio-btls" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e1fd638ec35427faf3b8f412e0fdd6fae76591d79dba40f38fa667d22bc44dd" -dependencies = [ - "btls", - "tokio", -] - [[package]] name = "tokio-macros" version = "2.7.2" @@ -1631,14 +1776,12 @@ dependencies = [ ] [[package]] -name = "tokio-socks" -version = "0.5.2" +name = "tokio-rustls" +version = "0.26.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" +checksum = "c9cc2678c2cdd569ef8215e2afd7954ada2ae20b4fdd2c5fe6139a3b02d105db" dependencies = [ - "either", - "futures-util", - "thiserror", + "rustls", "tokio", ] @@ -1670,6 +1813,24 @@ dependencies = [ "tower-service", ] +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -1682,6 +1843,25 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -1724,11 +1904,21 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +checksum = "ec4cdd0dd910afe868b7ef477227d8d538b46b3075031afee8a9f2acb0a2ed0b" dependencies = [ "try-lock", ] @@ -1804,35 +1994,55 @@ dependencies = [ ] [[package]] -name = "webpki-root-certs" -version = "1.0.9" +name = "wasm-streams" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" dependencies = [ - "rustls-pki-types", + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", ] [[package]] -name = "winapi" -version = "0.3.9" +name = "web-sys" +version = "0.3.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "84cde8507f4d7cfcb1185b8cb5890c494ffea65edbe1ba82cfd63661c805ed94" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "js-sys", + "wasm-bindgen", ] [[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" +name = "web-time" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "webpki-root-certs" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b" +dependencies = [ + "rustls-pki-types", +] + +[[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 = "windows-link" @@ -1928,70 +2138,6 @@ version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -[[package]] -name = "wreq" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16454175c8cb23d4a7285bab53b72460686195f5aef655d1abad70ab97ef46d4" -dependencies = [ - "btls", - "btls-sys", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "http2", - "httparse", - "ipnet", - "libc", - "lru", - "percent-encoding", - "pin-project-lite", - "socket2", - "sync_wrapper", - "tokio", - "tokio-btls", - "tokio-socks", - "tokio-util", - "tower", - "url", - "webpki-root-certs", - "wreq-proto", - "wreq-rt", -] - -[[package]] -name = "wreq-proto" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4d829f0427a5064829976fe790641ca21abb1d83d844812baf022dc0bdf2db" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "http2", - "httparse", - "pin-project-lite", - "smallvec", - "tokio", - "tokio-util", - "want", -] - -[[package]] -name = "wreq-rt" -version = "0.2.2-rc.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e9bce67a3fa3dd3f1503f066d86661c9caf399a763d3bd184da7afaf886c8b" -dependencies = [ - "pin-project-lite", - "tokio", - "wreq-proto", -] - [[package]] name = "writeable" version = "0.6.2" diff --git a/Cargo.toml b/Cargo.toml index 8593a72..7ca0649 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ futures = "0.3" pyo3 = { version = "0.29.3" } rsloop = { version = "0.1.56" } tokio = { version = "1", features = ["full"] } -wreq = { version = "0.16.1", features = ["socks", "stream"] } +reqwest = { version = "0.13.5", default-features = false, features = ["rustls", "http2", "socks", "stream"] } url = "2" diff --git a/README.md b/README.md index 586e376..8bdb1a7 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,8 @@ as soon as each proxy finishes. It uses: - `rsloop::rust_async::future_into_py(...)` to expose the async work to Python -- `wreq` for the HTTP client and proxy support -- a dedicated Tokio runtime inside the Rust worker because `wreq` runs on Tokio +- `reqwest` for the HTTP client and proxy support +- a dedicated Tokio runtime inside the Rust worker because `reqwest` runs on Tokio Each stream uses a current-thread Tokio runtime with I/O and timers enabled. Python result delivery runs on Tokio’s blocking pool. Pending result delivery is @@ -159,14 +159,15 @@ bash scripts/coverage.sh ## Supported proxy strings -The example passes the proxy string directly to `wreq`, so support follows the -proxy schemes that `wreq` accepts. In practice that includes normal HTTP/HTTPS +The example passes the proxy string directly to `reqwest`, so support follows the +proxy schemes that `reqwest` accepts. In practice that includes normal HTTP/HTTPS proxies and, with the enabled `socks` feature, SOCKS proxies as well. ## Development -Requires Python 3.10+ and Rust 1.98+. Native builds also need CMake, a C/C++ -compiler, and libclang (on Ubuntu: `sudo apt-get install cmake clang libclang-dev`). +Requires Python 3.10+ and Rust 1.98+. TLS uses rustls with the AWS-LC +crypto provider, also used by rsloop. Native builds need a C compiler +and may need CMake for AWS-LC. OpenSSL, BoringSSL and libclang are not required. ```bash cargo fmt --check diff --git a/src/checker.rs b/src/checker.rs index 06f1f6a..518320b 100644 --- a/src/checker.rs +++ b/src/checker.rs @@ -3,7 +3,7 @@ use std::time::Instant; use futures::StreamExt; -use wreq::header; +use reqwest::header; use crate::config::{CheckerConfig, REQUEST_BODY}; @@ -18,8 +18,12 @@ pub(crate) struct ProxyOutcome { } /// Build a Tokio HTTP client with the configured timeout and disabled idle pooling. -pub(crate) fn build_client(config: &CheckerConfig) -> Result { - let mut builder = wreq::Client::builder() +pub(crate) fn build_client( + config: &CheckerConfig, + proxy: Option, +) -> Result { + let mut builder = reqwest::Client::builder() + .tls_backend_rustls() .user_agent(config.user_agent.clone()) .no_proxy() .pool_max_idle_per_host(0) @@ -31,16 +35,19 @@ pub(crate) fn build_client(config: &CheckerConfig) -> Result Result { - // Preserve support for host:port inputs while rejecting schemes that wreq's +fn parse_proxy(proxy: &str) -> Result { + // Preserve support for host:port inputs while rejecting schemes that reqwest's // matcher silently ignores (which would otherwise send the request directly). let uri = if proxy.contains("://") { url::Url::parse(proxy) @@ -48,23 +55,22 @@ fn parse_proxy(proxy: &str) -> Result { url::Url::parse(&format!("http://{proxy}")) } .map_err(|err| format!("invalid proxy URL: {err}"))?; + if uri.host_str().is_none_or(str::is_empty) { + return Err("invalid proxy URL: missing host".into()); + } if !matches!( uri.scheme(), "http" | "https" | "socks4" | "socks4a" | "socks5" | "socks5h" ) { return Err(format!("unsupported proxy scheme: {}", uri.scheme())); } - wreq::Proxy::all(uri.as_str()).map_err(|err| err.to_string()) + reqwest::Proxy::all(uri.as_str()).map_err(|err| err.to_string()) } /// POST through one proxy, drain its body, and record transport or HTTP failures. /// /// Body-read failures override status-based success; text is collected only when requested. -pub(crate) async fn check_one_proxy( - client: wreq::Client, - config: CheckerConfig, - proxy: String, -) -> ProxyOutcome { +pub(crate) async fn check_one_proxy(config: CheckerConfig, proxy: String) -> ProxyOutcome { let started = Instant::now(); let request_proxy = match parse_proxy(&proxy) { Ok(request_proxy) => request_proxy, @@ -79,12 +85,23 @@ pub(crate) async fn check_one_proxy( }; } }; + let client = match build_client(&config, Some(request_proxy)) { + Ok(client) => client, + Err(err) => { + return ProxyOutcome { + proxy, + elapsed_ms: started.elapsed().as_millis(), + status: None, + ok: false, + error: Some(err), + response_text: None, + }; + } + }; let request = client .post(config.check_url.clone()) - .proxy(request_proxy) .header(header::CONTENT_TYPE, "text/plain; charset=utf-8") .timeout(config.timeout) - .read_timeout(config.timeout) .body(REQUEST_BODY); match request.send().await { diff --git a/src/checker_tests.rs b/src/checker_tests.rs index 356ad71..f7f909b 100644 --- a/src/checker_tests.rs +++ b/src/checker_tests.rs @@ -79,7 +79,7 @@ async fn check_response(status: u16, return_response: bool, truncated: bool) -> concurrency: 1, return_response, }; - let outcome = check_one_proxy(build_client(&config).unwrap(), config, proxy.clone()).await; + let outcome = check_one_proxy(config, proxy.clone()).await; server.await.unwrap(); assert_eq!(outcome.proxy, proxy); assert_eq!(outcome.status, Some(status)); @@ -147,7 +147,7 @@ fn malformed_proxy_and_invalid_user_agent_are_rejected() { } let mut config = config_for("http://target.invalid/check"); config.user_agent = "invalid\nheader".into(); - assert!(build_client(&config).is_err()); + assert!(build_client(&config, None).is_err()); } #[tokio::test] @@ -157,7 +157,7 @@ async fn invalid_proxy_and_transport_errors_have_no_http_status() { drop(listener); for proxy in ["invalid://proxy".to_string(), unavailable] { let config = config_for("http://target.invalid/check"); - let outcome = check_one_proxy(build_client(&config).unwrap(), config, proxy.clone()).await; + let outcome = check_one_proxy(config, proxy.clone()).await; assert!(!outcome.ok); assert_eq!(outcome.proxy, proxy); assert!(outcome.error.is_some()); diff --git a/src/worker.rs b/src/worker.rs index 987f9ea..2dafe66 100644 --- a/src/worker.rs +++ b/src/worker.rs @@ -46,7 +46,7 @@ fn run_proxy_checks_blocking( let runtime = TokioRuntimeBuilder::new_current_thread() .enable_all() .build() - .map_err(|err| format!("failed to build tokio runtime for wreq: {err}"))?; + .map_err(|err| format!("failed to build tokio runtime for reqwest: {err}"))?; let (loop_obj, queue) = Python::attach(|py| { ( @@ -71,13 +71,13 @@ async fn run_proxy_checks_async( queue: Arc>, credits: Arc, ) -> Result<(), String> { - let client = build_client(&config)?; + // Validate configuration before starting even an empty proxy batch. + drop(build_client(&config, None)?); let concurrency = config.concurrency.min(proxies.len().max(1)); let mut outcomes = stream::iter(proxies.into_iter().map(|proxy| { - let client = client.clone(); let config = config.clone(); - async move { check_one_proxy(client, config, proxy).await } + async move { check_one_proxy(config, proxy).await } })) .buffer_unordered(concurrency) .boxed(); diff --git a/tests/test_api.py b/tests/test_api.py index ed45479..7b64da9 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -165,7 +165,7 @@ async def run(): def test_worker_setup_error_raises_runtime_error(self): async def run(): stream = await checker.check_proxies([], user_agent="invalid\nheader") - with self.assertRaisesRegex(RuntimeError, "failed to build wreq client"): + with self.assertRaisesRegex(RuntimeError, "failed to build reqwest client"): [item async for item in stream] asyncio.run(run())