feat(downloads): switch to rqbit fork and implement fake tracker
- Update librqbit dependency to JakeHillion/rqbit fork (v9.0.0-beta.1) - Update session.rs for new librqbit API: - SessionPersistenceConfig instead of bool - AddTorrentResponse enum handling - Updated TorrentStats field access - Implement bencode module with AnnounceParams and BencodeValue encoding - Implement fake tracker that generates bencoded announce responses from Exo topology data (PeerInfo, TopologyData, handle_announce) - Fix PyO3 bindings for new librqbit types (remove allow_threads)
This commit is contained in:
Generated
+368
-59
@@ -296,6 +296,15 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atoi"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atomic-waker"
|
||||
version = "1.1.2"
|
||||
@@ -321,14 +330,87 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "backoff"
|
||||
version = "0.4.0"
|
||||
name = "axum"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1"
|
||||
checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8"
|
||||
dependencies = [
|
||||
"getrandom 0.2.16",
|
||||
"instant",
|
||||
"rand 0.8.5",
|
||||
"axum-core",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"itoa",
|
||||
"matchit",
|
||||
"memchr",
|
||||
"mime",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"serde_core",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-core"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"mime",
|
||||
"pin-project-lite",
|
||||
"sync_wrapper",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-extra"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9963ff19f40c6102c76756ef0a46004c0d58957d87259fc9208ff8441c12ab96"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"axum-core",
|
||||
"bytes",
|
||||
"form_urlencoded",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"mime",
|
||||
"pin-project-lite",
|
||||
"rustversion",
|
||||
"serde_core",
|
||||
"serde_html_form",
|
||||
"serde_path_to_error",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backon"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cffb0e931875b666fc4fcb20fee52e9bbd1ef836fd9e9e04ec21555f9f85f7ef"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"gloo-timers 0.3.0",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -384,15 +466,6 @@ version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "2.0.1"
|
||||
@@ -576,6 +649,17 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chardetng"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14b8f0b65b7b08ae3c8187e8d77174de20cb6777864c6b832d8ad365999cf1ea"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"encoding_rs",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.42"
|
||||
@@ -1068,6 +1152,18 @@ dependencies = [
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dontfrag"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c117949f5a8b25ba471b4dfea927a07a2f8730c585532a2eb4294e18f5155397"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"tokio",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "downloads"
|
||||
version = "0.0.1"
|
||||
@@ -1162,6 +1258,15 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enum-as-inner"
|
||||
version = "0.6.1"
|
||||
@@ -1466,7 +1571,7 @@ version = "3.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
|
||||
dependencies = [
|
||||
"gloo-timers",
|
||||
"gloo-timers 0.2.6",
|
||||
"send_wrapper 0.4.0",
|
||||
]
|
||||
|
||||
@@ -1577,6 +1682,18 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gloo-timers"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "governor"
|
||||
version = "0.10.4"
|
||||
@@ -1810,6 +1927,12 @@ version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
|
||||
|
||||
[[package]]
|
||||
name = "httpdate"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.8.1"
|
||||
@@ -1824,6 +1947,7 @@ dependencies = [
|
||||
"http",
|
||||
"http-body",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
@@ -2129,15 +2253,6 @@ dependencies = [
|
||||
"generic-array 0.14.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "internment"
|
||||
version = "0.8.6"
|
||||
@@ -3056,18 +3171,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "librqbit"
|
||||
version = "8.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dadca8f521242010a4c846ef5f224c217009c92e272709cdc08ba9cdabe62983"
|
||||
version = "9.0.0-beta.1"
|
||||
source = "git+https://github.com/JakeHillion/rqbit?rev=c4e2ecf81d03bd8acd96a0803d06a70b34d5da19#c4e2ecf81d03bd8acd96a0803d06a70b34d5da19"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
"async-compression",
|
||||
"async-stream",
|
||||
"async-trait",
|
||||
"backoff",
|
||||
"axum-extra",
|
||||
"backon",
|
||||
"base64",
|
||||
"bincode 2.0.1",
|
||||
"bincode",
|
||||
"bitvec",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
@@ -3083,22 +3198,29 @@ dependencies = [
|
||||
"librqbit-clone-to-owned",
|
||||
"librqbit-core",
|
||||
"librqbit-dht",
|
||||
"librqbit-dualstack-sockets",
|
||||
"librqbit-lsd",
|
||||
"librqbit-peer-protocol",
|
||||
"librqbit-sha1-wrapper",
|
||||
"librqbit-tracker-comms",
|
||||
"librqbit-upnp",
|
||||
"librqbit-utp",
|
||||
"memmap2",
|
||||
"mime_guess",
|
||||
"nix 0.30.1",
|
||||
"parking_lot",
|
||||
"rand 0.9.2",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"rlimit",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"serde_with",
|
||||
"size_format",
|
||||
"socket2 0.6.1",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"tokio-socks",
|
||||
"tokio-stream",
|
||||
@@ -3108,38 +3230,40 @@ dependencies = [
|
||||
"urlencoding",
|
||||
"uuid",
|
||||
"walkdir",
|
||||
"windows 0.62.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "librqbit-bencode"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "606dff526ba81e3eca33e2bb28b53afa2bc0b2c41d252333fa44e6c11abb37da"
|
||||
source = "git+https://github.com/JakeHillion/rqbit?rev=c4e2ecf81d03bd8acd96a0803d06a70b34d5da19#c4e2ecf81d03bd8acd96a0803d06a70b34d5da19"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arrayvec",
|
||||
"atoi",
|
||||
"bytes",
|
||||
"librqbit-buffers",
|
||||
"librqbit-clone-to-owned",
|
||||
"librqbit-sha1-wrapper",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"thiserror 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "librqbit-buffers"
|
||||
version = "4.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d78c78b907d6171a7191c162b2b60db46d254ebde6a95282b77372af556c1463"
|
||||
source = "git+https://github.com/JakeHillion/rqbit?rev=c4e2ecf81d03bd8acd96a0803d06a70b34d5da19#c4e2ecf81d03bd8acd96a0803d06a70b34d5da19"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"librqbit-clone-to-owned",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "librqbit-clone-to-owned"
|
||||
version = "3.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbd1e66d773ba9c475ff89286dc1d6f9d167cbb898603797467dd0ea6844c445"
|
||||
source = "git+https://github.com/JakeHillion/rqbit?rev=c4e2ecf81d03bd8acd96a0803d06a70b34d5da19#c4e2ecf81d03bd8acd96a0803d06a70b34d5da19"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
]
|
||||
@@ -3147,22 +3271,26 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "librqbit-core"
|
||||
version = "5.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55a02cc6fce6743ad38661ccd6fafc6cf1ae5e0106a9922836b0524dbe752378"
|
||||
source = "git+https://github.com/JakeHillion/rqbit?rev=c4e2ecf81d03bd8acd96a0803d06a70b34d5da19#c4e2ecf81d03bd8acd96a0803d06a70b34d5da19"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_cfg",
|
||||
"bytes",
|
||||
"chardetng",
|
||||
"data-encoding",
|
||||
"directories",
|
||||
"encoding_rs",
|
||||
"hex 0.4.3",
|
||||
"itertools 0.14.0",
|
||||
"librqbit-bencode",
|
||||
"librqbit-buffers",
|
||||
"librqbit-clone-to-owned",
|
||||
"librqbit-sha1-wrapper",
|
||||
"memchr",
|
||||
"parking_lot",
|
||||
"rand 0.9.2",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
@@ -3171,27 +3299,28 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "librqbit-dht"
|
||||
version = "5.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7cc129194337771a86b0399956c4d9bf1cd97c5f24d14a50be38e170f76a54b"
|
||||
version = "5.3.0"
|
||||
source = "git+https://github.com/JakeHillion/rqbit?rev=c4e2ecf81d03bd8acd96a0803d06a70b34d5da19#c4e2ecf81d03bd8acd96a0803d06a70b34d5da19"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"backoff",
|
||||
"byteorder",
|
||||
"backon",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"dashmap",
|
||||
"futures",
|
||||
"hex 0.4.3",
|
||||
"indexmap 2.12.1",
|
||||
"leaky-bucket",
|
||||
"librqbit-bencode",
|
||||
"librqbit-buffers",
|
||||
"librqbit-clone-to-owned",
|
||||
"librqbit-core",
|
||||
"librqbit-dualstack-sockets",
|
||||
"parking_lot",
|
||||
"rand 0.9.2",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tokio-util",
|
||||
@@ -3199,13 +3328,48 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "librqbit-peer-protocol"
|
||||
version = "4.3.0"
|
||||
name = "librqbit-dualstack-sockets"
|
||||
version = "0.6.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a73129497b500505f33d1dc0426319b6a6a208f13fdfaae56224ab8c2346a773"
|
||||
checksum = "52135d1583bc772b74d5dc72a75ca8ed810463159c028d0b28609f597fffa816"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"backon",
|
||||
"futures",
|
||||
"libc",
|
||||
"network-interface",
|
||||
"socket2 0.6.1",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "librqbit-lsd"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/JakeHillion/rqbit?rev=c4e2ecf81d03bd8acd96a0803d06a70b34d5da19#c4e2ecf81d03bd8acd96a0803d06a70b34d5da19"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"atoi",
|
||||
"bstr",
|
||||
"futures",
|
||||
"httparse",
|
||||
"librqbit-core",
|
||||
"librqbit-dualstack-sockets",
|
||||
"librqbit-sha1-wrapper",
|
||||
"parking_lot",
|
||||
"rand 0.9.2",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "librqbit-peer-protocol"
|
||||
version = "4.3.0"
|
||||
source = "git+https://github.com/JakeHillion/rqbit?rev=c4e2ecf81d03bd8acd96a0803d06a70b34d5da19#c4e2ecf81d03bd8acd96a0803d06a70b34d5da19"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode 1.3.3",
|
||||
"bitvec",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
@@ -3215,13 +3379,15 @@ dependencies = [
|
||||
"librqbit-clone-to-owned",
|
||||
"librqbit-core",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"thiserror 2.0.17",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "librqbit-sha1-wrapper"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79373a02db73159e4de7ca5d27b6eeae2d540df66c6801db2b01c5513d087524"
|
||||
source = "git+https://github.com/JakeHillion/rqbit?rev=c4e2ecf81d03bd8acd96a0803d06a70b34d5da19#c4e2ecf81d03bd8acd96a0803d06a70b34d5da19"
|
||||
dependencies = [
|
||||
"assert_cfg",
|
||||
"crypto-hash",
|
||||
@@ -3230,20 +3396,24 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "librqbit-tracker-comms"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08204944c5be677a5de8e1230e0249fce5c14abef23048e26452c6fb03f1b260"
|
||||
source = "git+https://github.com/JakeHillion/rqbit?rev=c4e2ecf81d03bd8acd96a0803d06a70b34d5da19#c4e2ecf81d03bd8acd96a0803d06a70b34d5da19"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
"backon",
|
||||
"byteorder",
|
||||
"futures",
|
||||
"itertools 0.14.0",
|
||||
"librqbit-bencode",
|
||||
"librqbit-buffers",
|
||||
"librqbit-core",
|
||||
"librqbit-dualstack-sockets",
|
||||
"parking_lot",
|
||||
"rand 0.9.2",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_with",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
@@ -3254,22 +3424,47 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "librqbit-upnp"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "545aad6124c97201055983137e12a19f34acad565120c3cd30596cbd72e8fa86"
|
||||
source = "git+https://github.com/JakeHillion/rqbit?rev=c4e2ecf81d03bd8acd96a0803d06a70b34d5da19#c4e2ecf81d03bd8acd96a0803d06a70b34d5da19"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bstr",
|
||||
"futures",
|
||||
"httparse",
|
||||
"librqbit-dualstack-sockets",
|
||||
"network-interface",
|
||||
"quick-xml",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"socket2 0.6.1",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "librqbit-utp"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7bf54992c5b2c053c50782f6d14164439f46cd889fb97de25ee0bdee04fd5b33"
|
||||
dependencies = [
|
||||
"bitvec",
|
||||
"dontfrag",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"librqbit-dualstack-sockets",
|
||||
"metrics",
|
||||
"parking_lot",
|
||||
"rand 0.9.2",
|
||||
"ringbuf",
|
||||
"rustc-hash 2.1.1",
|
||||
"socket2 0.6.1",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.11.0"
|
||||
@@ -3338,6 +3533,12 @@ dependencies = [
|
||||
"regex-automata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matchit"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
|
||||
|
||||
[[package]]
|
||||
name = "matrixmultiply"
|
||||
version = "0.3.10"
|
||||
@@ -3382,6 +3583,16 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "metrics"
|
||||
version = "0.24.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d5312e9ba3771cfa961b585728215e3d972c950a3eed9252aa093d6301277e8"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"portable-atomic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.17"
|
||||
@@ -3638,6 +3849,18 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.30.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nohash-hasher"
|
||||
version = "0.2.0"
|
||||
@@ -4367,9 +4590,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.37.5"
|
||||
version = "0.38.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb"
|
||||
checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"serde",
|
||||
@@ -4744,6 +4967,17 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ringbuf"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe47b720588c8702e34b5979cb3271a8b1842c7cb6f57408efa70c779363488c"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
"portable-atomic",
|
||||
"portable-atomic-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rlimit"
|
||||
version = "0.10.2"
|
||||
@@ -4766,7 +5000,7 @@ dependencies = [
|
||||
"netlink-packet-utils",
|
||||
"netlink-proto",
|
||||
"netlink-sys",
|
||||
"nix",
|
||||
"nix 0.26.4",
|
||||
"thiserror 1.0.69",
|
||||
"tokio",
|
||||
]
|
||||
@@ -5084,6 +5318,19 @@ dependencies = [
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_html_form"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2f2d7ff8a2140333718bb329f5c40fc5f0865b84c426183ce14c97d2ab8154f"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"indexmap 2.12.1",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.145"
|
||||
@@ -5097,6 +5344,17 @@ dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_path_to_error"
|
||||
version = "0.1.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "1.0.3"
|
||||
@@ -6274,6 +6532,27 @@ dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.62.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
|
||||
dependencies = [
|
||||
"windows-collections",
|
||||
"windows-core 0.62.2",
|
||||
"windows-future",
|
||||
"windows-numerics",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-collections"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
|
||||
dependencies = [
|
||||
"windows-core 0.62.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.53.0"
|
||||
@@ -6309,6 +6588,17 @@ dependencies = [
|
||||
"windows-strings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-future"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
|
||||
dependencies = [
|
||||
"windows-core 0.62.2",
|
||||
"windows-link",
|
||||
"windows-threading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.57.0"
|
||||
@@ -6359,6 +6649,16 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-numerics"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
|
||||
dependencies = [
|
||||
"windows-core 0.62.2",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.1.2"
|
||||
@@ -6470,6 +6770,15 @@ dependencies = [
|
||||
"windows_x86_64_msvc 0.53.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-threading"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
|
||||
@@ -31,7 +31,7 @@ itertools = { workspace = true }
|
||||
log = { workspace = true }
|
||||
|
||||
# BitTorrent library
|
||||
librqbit = "8.1.1"
|
||||
librqbit = { git = "https://github.com/JakeHillion/rqbit", rev = "c4e2ecf81d03bd8acd96a0803d06a70b34d5da19" }
|
||||
|
||||
# Embed torrent files
|
||||
include_dir = "0.7"
|
||||
|
||||
@@ -1 +1,162 @@
|
||||
//! Bencode encoding for BitTorrent tracker responses
|
||||
//!
|
||||
//! Implements the subset of bencoding needed for tracker announce responses.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
/// Parameters from a tracker announce request
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AnnounceParams {
|
||||
/// 20-byte info hash of the torrent
|
||||
pub info_hash: [u8; 20],
|
||||
/// 20-byte peer ID of the client
|
||||
pub peer_id: [u8; 20],
|
||||
/// Port the client is listening on
|
||||
pub port: u16,
|
||||
/// Total bytes uploaded
|
||||
pub uploaded: u64,
|
||||
/// Total bytes downloaded
|
||||
pub downloaded: u64,
|
||||
/// Bytes remaining to download
|
||||
pub left: u64,
|
||||
/// Whether to return compact peer list (6 bytes per peer)
|
||||
pub compact: bool,
|
||||
/// Optional event (started, stopped, completed)
|
||||
pub event: Option<AnnounceEvent>,
|
||||
}
|
||||
|
||||
/// Announce event types
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum AnnounceEvent {
|
||||
Started,
|
||||
Stopped,
|
||||
Completed,
|
||||
}
|
||||
|
||||
/// A bencoded value
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum BencodeValue {
|
||||
Integer(i64),
|
||||
Bytes(Vec<u8>),
|
||||
List(Vec<BencodeValue>),
|
||||
Dict(BTreeMap<Vec<u8>, BencodeValue>),
|
||||
}
|
||||
|
||||
impl BencodeValue {
|
||||
/// Create a string value from a &str
|
||||
#[inline]
|
||||
pub fn string(s: &str) -> Self {
|
||||
Self::Bytes(s.as_bytes().to_vec())
|
||||
}
|
||||
|
||||
/// Create an integer value
|
||||
#[inline]
|
||||
pub fn integer(i: i64) -> Self {
|
||||
Self::Integer(i)
|
||||
}
|
||||
|
||||
/// Create an empty list
|
||||
#[inline]
|
||||
pub fn list() -> Self {
|
||||
Self::List(Vec::new())
|
||||
}
|
||||
|
||||
/// Create an empty dict
|
||||
#[inline]
|
||||
pub fn dict() -> Self {
|
||||
Self::Dict(BTreeMap::new())
|
||||
}
|
||||
|
||||
/// Add an item to a list (builder pattern)
|
||||
#[inline]
|
||||
pub fn push(mut self, value: BencodeValue) -> Self {
|
||||
if let Self::List(ref mut list) = self {
|
||||
list.push(value);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Insert a key-value pair into a dict (builder pattern)
|
||||
#[inline]
|
||||
pub fn insert(mut self, key: &str, value: BencodeValue) -> Self {
|
||||
if let Self::Dict(ref mut dict) = self {
|
||||
dict.insert(key.as_bytes().to_vec(), value);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Encode to bencoded bytes
|
||||
pub fn encode(&self) -> Vec<u8> {
|
||||
let mut buf = Vec::new();
|
||||
self.encode_into(&mut buf);
|
||||
buf
|
||||
}
|
||||
|
||||
/// Encode into an existing buffer
|
||||
pub fn encode_into(&self, buf: &mut Vec<u8>) {
|
||||
match self {
|
||||
Self::Integer(i) => {
|
||||
buf.push(b'i');
|
||||
buf.extend_from_slice(i.to_string().as_bytes());
|
||||
buf.push(b'e');
|
||||
}
|
||||
Self::Bytes(bytes) => {
|
||||
buf.extend_from_slice(bytes.len().to_string().as_bytes());
|
||||
buf.push(b':');
|
||||
buf.extend_from_slice(bytes);
|
||||
}
|
||||
Self::List(list) => {
|
||||
buf.push(b'l');
|
||||
for item in list {
|
||||
item.encode_into(buf);
|
||||
}
|
||||
buf.push(b'e');
|
||||
}
|
||||
Self::Dict(dict) => {
|
||||
buf.push(b'd');
|
||||
// BTreeMap keeps keys sorted
|
||||
for (key, value) in dict {
|
||||
buf.extend_from_slice(key.len().to_string().as_bytes());
|
||||
buf.push(b':');
|
||||
buf.extend_from_slice(key);
|
||||
value.encode_into(buf);
|
||||
}
|
||||
buf.push(b'e');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_encode_integer() {
|
||||
assert_eq!(BencodeValue::integer(42).encode(), b"i42e");
|
||||
assert_eq!(BencodeValue::integer(-1).encode(), b"i-1e");
|
||||
assert_eq!(BencodeValue::integer(0).encode(), b"i0e");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_string() {
|
||||
assert_eq!(BencodeValue::string("spam").encode(), b"4:spam");
|
||||
assert_eq!(BencodeValue::string("").encode(), b"0:");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_list() {
|
||||
let list = BencodeValue::list()
|
||||
.push(BencodeValue::string("spam"))
|
||||
.push(BencodeValue::integer(42));
|
||||
assert_eq!(list.encode(), b"l4:spami42ee");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_dict() {
|
||||
let dict = BencodeValue::dict()
|
||||
.insert("bar", BencodeValue::string("spam"))
|
||||
.insert("foo", BencodeValue::integer(42));
|
||||
assert_eq!(dict.encode(), b"d3:bar4:spam3:fooi42ee");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ pub mod progress;
|
||||
pub mod session;
|
||||
pub mod tracker;
|
||||
|
||||
pub use bencode::AnnounceParams;
|
||||
pub use embedded::get_embedded_torrent;
|
||||
pub use session::{DownloadProgress, TorrentSession};
|
||||
pub use tracker::handle_announce;
|
||||
pub use tracker::{handle_announce, PeerInfo, TopologyData};
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//! with persistent seeding and selective file downloads.
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use librqbit::{AddTorrent, AddTorrentOptions, Api, ManagedTorrentHandle, Session, SessionOptions};
|
||||
use librqbit::{AddTorrent, AddTorrentOptions, AddTorrentResponse, Api, ManagedTorrent, Session, SessionOptions, SessionPersistenceConfig};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
@@ -27,7 +27,7 @@ pub struct TorrentSession {
|
||||
session: Arc<Session>,
|
||||
api: Arc<Api>,
|
||||
session_dir: PathBuf,
|
||||
torrents: Arc<RwLock<HashMap<String, ManagedTorrentHandle>>>,
|
||||
torrents: Arc<RwLock<HashMap<String, Arc<ManagedTorrent>>>>,
|
||||
}
|
||||
|
||||
impl TorrentSession {
|
||||
@@ -42,7 +42,7 @@ impl TorrentSession {
|
||||
disable_dht: false,
|
||||
disable_dht_persistence: false,
|
||||
dht_config: None,
|
||||
persistence: true,
|
||||
persistence: Some(SessionPersistenceConfig::Json { folder: None }),
|
||||
fastresume: true,
|
||||
..Default::default()
|
||||
};
|
||||
@@ -54,7 +54,7 @@ impl TorrentSession {
|
||||
let api = Api::new(Arc::clone(&session), None);
|
||||
|
||||
Ok(Self {
|
||||
session: Arc::new(session),
|
||||
session,
|
||||
api: Arc::new(api),
|
||||
session_dir,
|
||||
torrents: Arc::new(RwLock::new(HashMap::new())),
|
||||
@@ -79,20 +79,25 @@ impl TorrentSession {
|
||||
let opts = AddTorrentOptions {
|
||||
overwrite: false,
|
||||
only_files_regex: None,
|
||||
only_files: file_indices
|
||||
.map(|indices| librqbit::AddTorrentOptions::only_files_from_vec(indices)),
|
||||
only_files: file_indices,
|
||||
output_folder: Some(save_path.to_string_lossy().to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let add_torrent = AddTorrent::from_bytes(torrent_data);
|
||||
|
||||
let handle = self
|
||||
let response = self
|
||||
.session
|
||||
.add_torrent(add_torrent, Some(opts))
|
||||
.await
|
||||
.context("Failed to add torrent")?;
|
||||
|
||||
let handle = match response {
|
||||
AddTorrentResponse::Added(_, handle) => handle,
|
||||
AddTorrentResponse::AlreadyManaged(_, handle) => handle,
|
||||
AddTorrentResponse::ListOnly(_) => anyhow::bail!("Torrent was list-only, not added"),
|
||||
};
|
||||
|
||||
let info_hash = handle.info_hash().as_string();
|
||||
|
||||
self.torrents
|
||||
@@ -109,15 +114,14 @@ impl TorrentSession {
|
||||
let handle = torrents.get(info_hash).context("Torrent not found")?;
|
||||
|
||||
let stats = handle.stats();
|
||||
let state = handle.state();
|
||||
|
||||
Ok(DownloadProgress {
|
||||
downloaded_bytes: stats.downloaded_bytes,
|
||||
downloaded_bytes: stats.progress_bytes,
|
||||
total_bytes: stats.total_bytes,
|
||||
download_speed: stats.download_speed,
|
||||
upload_speed: stats.upload_speed,
|
||||
peers_connected: stats.peers_connected,
|
||||
is_finished: state.is_finished(),
|
||||
download_speed: stats.live.as_ref().map_or(0.0, |l| l.download_speed.mbps * 1024.0 * 1024.0),
|
||||
upload_speed: stats.live.as_ref().map_or(0.0, |l| l.upload_speed.mbps * 1024.0 * 1024.0),
|
||||
peers_connected: stats.live.as_ref().map_or(0, |l| l.snapshot.peer_stats.live as usize),
|
||||
is_finished: stats.finished,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +1,185 @@
|
||||
//! Fake tracker implementation for Exo topology-based peer discovery
|
||||
//!
|
||||
//! Instead of contacting real BitTorrent trackers, this module generates
|
||||
//! tracker announce responses using Exo's cluster topology data.
|
||||
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::bencode::{AnnounceParams, BencodeValue};
|
||||
|
||||
/// Information about a peer in the Exo topology
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PeerInfo {
|
||||
/// Unique node identifier in the Exo cluster
|
||||
pub node_id: String,
|
||||
/// IPv4 address of the peer
|
||||
pub ip: Ipv4Addr,
|
||||
/// BitTorrent listening port
|
||||
pub port: u16,
|
||||
/// Whether this peer has the complete torrent
|
||||
pub has_complete: bool,
|
||||
/// Priority for peer selection (higher = prefer)
|
||||
pub priority: i32,
|
||||
}
|
||||
|
||||
/// Topology data containing available peers
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TopologyData {
|
||||
/// List of peers in the topology
|
||||
pub peers: Vec<PeerInfo>,
|
||||
}
|
||||
|
||||
/// Default announce interval in seconds
|
||||
const DEFAULT_INTERVAL: i64 = 1800;
|
||||
|
||||
/// Handle a tracker announce request using Exo topology data
|
||||
///
|
||||
/// Returns a bencoded tracker response containing peers from the topology.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `params` - Announce request parameters
|
||||
/// * `topology` - Current Exo cluster topology
|
||||
///
|
||||
/// # Returns
|
||||
/// Bencoded announce response as bytes
|
||||
pub fn handle_announce(params: &AnnounceParams, topology: &TopologyData) -> Result<Vec<u8>> {
|
||||
// Sort peers by priority (descending) for better peer selection
|
||||
let mut peers: Vec<_> = topology.peers.iter().collect();
|
||||
peers.sort_by(|a, b| b.priority.cmp(&a.priority));
|
||||
|
||||
let response = if params.compact {
|
||||
// Compact format: 6 bytes per peer (4 IP + 2 port)
|
||||
let mut peer_data = Vec::with_capacity(peers.len() * 6);
|
||||
for peer in &peers {
|
||||
peer_data.extend_from_slice(&peer.ip.octets());
|
||||
peer_data.extend_from_slice(&peer.port.to_be_bytes());
|
||||
}
|
||||
|
||||
BencodeValue::dict()
|
||||
.insert("interval", BencodeValue::integer(DEFAULT_INTERVAL))
|
||||
.insert("peers", BencodeValue::Bytes(peer_data))
|
||||
} else {
|
||||
// Non-compact format: list of dicts
|
||||
let mut peer_list = BencodeValue::list();
|
||||
for peer in &peers {
|
||||
let peer_dict = BencodeValue::dict()
|
||||
.insert("ip", BencodeValue::string(&peer.ip.to_string()))
|
||||
.insert("port", BencodeValue::integer(i64::from(peer.port)))
|
||||
.insert("peer id", BencodeValue::Bytes(vec![0u8; 20])); // Placeholder peer ID
|
||||
peer_list = peer_list.push(peer_dict);
|
||||
}
|
||||
|
||||
BencodeValue::dict()
|
||||
.insert("interval", BencodeValue::integer(DEFAULT_INTERVAL))
|
||||
.insert("peers", peer_list)
|
||||
};
|
||||
|
||||
Ok(response.encode())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn make_test_params(compact: bool) -> AnnounceParams {
|
||||
AnnounceParams {
|
||||
info_hash: [0u8; 20],
|
||||
peer_id: [0u8; 20],
|
||||
port: 6881,
|
||||
uploaded: 0,
|
||||
downloaded: 0,
|
||||
left: 1000,
|
||||
compact,
|
||||
event: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn make_test_topology() -> TopologyData {
|
||||
TopologyData {
|
||||
peers: vec![
|
||||
PeerInfo {
|
||||
node_id: "node1".to_string(),
|
||||
ip: Ipv4Addr::new(192, 168, 1, 1),
|
||||
port: 6881,
|
||||
has_complete: true,
|
||||
priority: 10,
|
||||
},
|
||||
PeerInfo {
|
||||
node_id: "node2".to_string(),
|
||||
ip: Ipv4Addr::new(192, 168, 1, 2),
|
||||
port: 6882,
|
||||
has_complete: false,
|
||||
priority: 5,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compact_response() {
|
||||
let params = make_test_params(true);
|
||||
let topology = make_test_topology();
|
||||
|
||||
let response = handle_announce(¶ms, &topology).unwrap();
|
||||
|
||||
// Should contain "interval" and "peers" keys
|
||||
assert!(response.starts_with(b"d"));
|
||||
assert!(response.ends_with(b"e"));
|
||||
|
||||
// Verify we have 12 bytes of peer data (2 peers * 6 bytes)
|
||||
// The compact peers field should be "12:<12 bytes>"
|
||||
let response_str = String::from_utf8_lossy(&response);
|
||||
assert!(response_str.contains("8:interval"));
|
||||
assert!(response_str.contains("5:peers"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_non_compact_response() {
|
||||
let params = make_test_params(false);
|
||||
let topology = make_test_topology();
|
||||
|
||||
let response = handle_announce(¶ms, &topology).unwrap();
|
||||
|
||||
// Should contain peers as a list
|
||||
let response_str = String::from_utf8_lossy(&response);
|
||||
assert!(response_str.contains("8:interval"));
|
||||
assert!(response_str.contains("5:peers"));
|
||||
assert!(response_str.contains("2:ip"));
|
||||
assert!(response_str.contains("4:port"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_peer_priority_ordering() {
|
||||
let params = make_test_params(true);
|
||||
let topology = make_test_topology();
|
||||
|
||||
let response = handle_announce(¶ms, &topology).unwrap();
|
||||
|
||||
// In compact format, first peer should be node1 (priority 10)
|
||||
// which is 192.168.1.1:6881
|
||||
// Look for the peer data after "5:peers12:"
|
||||
let peers_marker = b"5:peers12:";
|
||||
let pos = response
|
||||
.windows(peers_marker.len())
|
||||
.position(|w| w == peers_marker)
|
||||
.unwrap();
|
||||
let peer_data = &response[pos + peers_marker.len()..pos + peers_marker.len() + 6];
|
||||
|
||||
// First peer should be 192.168.1.1 (node1 with higher priority)
|
||||
assert_eq!(&peer_data[0..4], &[192, 168, 1, 1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_topology() {
|
||||
let params = make_test_params(true);
|
||||
let topology = TopologyData { peers: vec![] };
|
||||
|
||||
let response = handle_announce(¶ms, &topology).unwrap();
|
||||
|
||||
// Should still be valid bencoded response with empty peers
|
||||
assert!(response.starts_with(b"d"));
|
||||
assert!(response.ends_with(b"e"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ impl TorrentSessionHandle {
|
||||
/// Info hash as hex string
|
||||
fn add_torrent(
|
||||
&self,
|
||||
py: Python<'_>,
|
||||
_py: Python<'_>,
|
||||
torrent_data: Vec<u8>,
|
||||
save_path: String,
|
||||
file_indices: Option<Vec<usize>>,
|
||||
@@ -216,18 +216,16 @@ impl TorrentSessionHandle {
|
||||
let session = Arc::clone(&self.session);
|
||||
let save_path = PathBuf::from(save_path);
|
||||
|
||||
py.allow_threads(|| {
|
||||
tokio::runtime::Runtime::new()
|
||||
.pyerr()?
|
||||
.block_on(async {
|
||||
session
|
||||
.lock()
|
||||
.await
|
||||
.add_torrent(torrent_data, save_path, file_indices)
|
||||
.await
|
||||
})
|
||||
.pyerr()
|
||||
})
|
||||
tokio::runtime::Runtime::new()
|
||||
.pyerr()?
|
||||
.block_on(async {
|
||||
session
|
||||
.lock()
|
||||
.await
|
||||
.add_torrent(torrent_data, save_path, file_indices)
|
||||
.await
|
||||
})
|
||||
.pyerr()
|
||||
}
|
||||
|
||||
/// Get download progress for a torrent
|
||||
@@ -240,12 +238,10 @@ impl TorrentSessionHandle {
|
||||
fn get_progress(&self, py: Python<'_>, info_hash: String) -> PyResult<Py<PyDict>> {
|
||||
let session = Arc::clone(&self.session);
|
||||
|
||||
let progress: DownloadProgress = py.allow_threads(|| {
|
||||
tokio::runtime::Runtime::new()
|
||||
.pyerr()?
|
||||
.block_on(async { session.lock().await.get_progress(&info_hash).await })
|
||||
.pyerr()
|
||||
})?;
|
||||
let progress: DownloadProgress = tokio::runtime::Runtime::new()
|
||||
.pyerr()?
|
||||
.block_on(async { session.lock().await.get_progress(&info_hash).await })
|
||||
.pyerr()?;
|
||||
|
||||
let dict = PyDict::new(py);
|
||||
dict.set_item("downloaded_bytes", progress.downloaded_bytes)?;
|
||||
@@ -262,59 +258,51 @@ impl TorrentSessionHandle {
|
||||
///
|
||||
/// Args:
|
||||
/// info_hash: Torrent info hash
|
||||
fn wait_until_completed(&self, py: Python<'_>, info_hash: String) -> PyResult<()> {
|
||||
fn wait_until_completed(&self, _py: Python<'_>, info_hash: String) -> PyResult<()> {
|
||||
let session = Arc::clone(&self.session);
|
||||
|
||||
py.allow_threads(|| {
|
||||
tokio::runtime::Runtime::new()
|
||||
.pyerr()?
|
||||
.block_on(async { session.lock().await.wait_until_completed(&info_hash).await })
|
||||
.pyerr()
|
||||
})
|
||||
tokio::runtime::Runtime::new()
|
||||
.pyerr()?
|
||||
.block_on(async { session.lock().await.wait_until_completed(&info_hash).await })
|
||||
.pyerr()
|
||||
}
|
||||
|
||||
/// Enable seeding for a torrent
|
||||
///
|
||||
/// Args:
|
||||
/// info_hash: Torrent info hash
|
||||
fn enable_seeding(&self, py: Python<'_>, info_hash: String) -> PyResult<()> {
|
||||
fn enable_seeding(&self, _py: Python<'_>, info_hash: String) -> PyResult<()> {
|
||||
let session = Arc::clone(&self.session);
|
||||
|
||||
py.allow_threads(|| {
|
||||
tokio::runtime::Runtime::new()
|
||||
.pyerr()?
|
||||
.block_on(async { session.lock().await.enable_seeding(&info_hash).await })
|
||||
.pyerr()
|
||||
})
|
||||
tokio::runtime::Runtime::new()
|
||||
.pyerr()?
|
||||
.block_on(async { session.lock().await.enable_seeding(&info_hash).await })
|
||||
.pyerr()
|
||||
}
|
||||
|
||||
/// Remove a torrent from the session
|
||||
///
|
||||
/// Args:
|
||||
/// info_hash: Torrent info hash
|
||||
fn remove_torrent(&self, py: Python<'_>, info_hash: String) -> PyResult<()> {
|
||||
fn remove_torrent(&self, _py: Python<'_>, info_hash: String) -> PyResult<()> {
|
||||
let session = Arc::clone(&self.session);
|
||||
|
||||
py.allow_threads(|| {
|
||||
tokio::runtime::Runtime::new()
|
||||
.pyerr()?
|
||||
.block_on(async { session.lock().await.remove_torrent(&info_hash).await })
|
||||
.pyerr()
|
||||
})
|
||||
tokio::runtime::Runtime::new()
|
||||
.pyerr()?
|
||||
.block_on(async { session.lock().await.remove_torrent(&info_hash).await })
|
||||
.pyerr()
|
||||
}
|
||||
|
||||
/// List all torrents in the session
|
||||
///
|
||||
/// Returns:
|
||||
/// List of info hashes
|
||||
fn list_torrents(&self, py: Python<'_>) -> PyResult<Vec<String>> {
|
||||
fn list_torrents(&self, _py: Python<'_>) -> PyResult<Vec<String>> {
|
||||
let session = Arc::clone(&self.session);
|
||||
|
||||
py.allow_threads(|| {
|
||||
tokio::runtime::Runtime::new()
|
||||
.pyerr()?
|
||||
.block_on(async { Ok(session.lock().await.list_torrents().await) })
|
||||
})
|
||||
tokio::runtime::Runtime::new()
|
||||
.pyerr()?
|
||||
.block_on(async { Ok(session.lock().await.list_torrents().await) })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user