├── .github ├── dependabot.yml └── workflows │ └── test.yml ├── .gitignore ├── .mergify.yml ├── Cargo.lock ├── Cargo.toml ├── Justfile ├── LICENSE ├── README.md ├── actix-bililive ├── Cargo.toml ├── LICENSE ├── README.md ├── examples │ └── simple.rs └── src │ ├── builder │ ├── awc.rs │ ├── mod.rs │ └── tests.rs │ ├── connect.rs │ ├── errors.rs │ ├── lib.rs │ └── stream │ ├── codec.rs │ ├── mod.rs │ ├── pingpong.rs │ └── tests.rs ├── bililive-core ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── src │ ├── builder │ │ ├── mod.rs │ │ ├── tests.rs │ │ └── types.rs │ ├── config.rs │ ├── errors.rs │ ├── lib.rs │ ├── packet │ │ ├── mod.rs │ │ ├── parser.rs │ │ ├── tests.rs │ │ └── types.rs │ ├── retry │ │ ├── config.rs │ │ ├── context.rs │ │ ├── mod.rs │ │ └── policy.rs │ └── stream │ │ ├── heartbeat.rs │ │ ├── mod.rs │ │ └── waker.rs └── tests │ ├── getConf.json │ └── raw │ ├── buffer.packet │ ├── int32be.packet │ └── json.packet ├── bililive ├── Cargo.toml ├── LICENSE ├── README.md ├── examples │ └── simple.rs └── src │ ├── builder │ ├── h1.rs │ ├── mod.rs │ ├── reqwest.rs │ └── tests.rs │ ├── connect.rs │ ├── errors.rs │ ├── lib.rs │ └── stream │ ├── codec.rs │ ├── mod.rs │ └── tests.rs └── rust-toolchain.toml /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "cargo" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | pull_request: 4 | branches: 5 | - master 6 | 7 | name: Test 8 | 9 | jobs: 10 | lint: 11 | name: Lint 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | name: Checkout 🛎️ 16 | - uses: actions-rs/toolchain@v1 17 | name: Setup Cargo Toolchain 🛎️ 18 | with: 19 | components: rustfmt, clippy 20 | profile: minimal 21 | toolchain: stable 22 | default: true 23 | - uses: extractions/setup-just@v1 24 | - uses: Swatinem/rust-cache@v1 25 | - name: Run Lints 🔨 26 | run: just ci 27 | 28 | test: 29 | name: Test 30 | runs-on: ubuntu-latest 31 | steps: 32 | - uses: actions/checkout@v2 33 | name: Checkout 🛎️ 34 | - uses: actions-rs/toolchain@v1 35 | name: Setup Cargo Toolchain 🛎️ 36 | with: 37 | profile: minimal 38 | toolchain: stable 39 | default: true 40 | - uses: extractions/setup-just@v1 41 | - uses: Swatinem/rust-cache@v1 42 | - name: Running 🚀 43 | run: just test-full 44 | 45 | doc_test: 46 | name: Doc 47 | runs-on: ubuntu-latest 48 | steps: 49 | - uses: actions/checkout@v2 50 | name: Checkout 🛎️ 51 | - uses: actions-rs/toolchain@v1 52 | name: Setup Cargo Toolchain 🛎️ 53 | with: 54 | components: rust-docs 55 | profile: minimal 56 | toolchain: stable 57 | default: true 58 | - uses: extractions/setup-just@v1 59 | - uses: Swatinem/rust-cache@v1 60 | - name: Building Docs 🚀 61 | run: just doc-all -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.idea 3 | /capture 4 | /raw_capture -------------------------------------------------------------------------------- /.mergify.yml: -------------------------------------------------------------------------------- 1 | pull_request_rules: 2 | - name: Automatic merge on approval 3 | conditions: 4 | - author=dependabot[bot] 5 | - check-success=Lint 6 | - check-success=Test 7 | - check-success=Doc 8 | actions: 9 | merge: 10 | method: squash -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "actix-bililive" 7 | version = "0.1.0-beta.8" 8 | dependencies = [ 9 | "actix-codec", 10 | "actix-rt", 11 | "awc", 12 | "bililive-core", 13 | "bytes", 14 | "futures", 15 | "log", 16 | "serde", 17 | "serde_json", 18 | "stream-reconnect", 19 | ] 20 | 21 | [[package]] 22 | name = "actix-codec" 23 | version = "0.5.2" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" 26 | dependencies = [ 27 | "bitflags 2.4.0", 28 | "bytes", 29 | "futures-core", 30 | "futures-sink", 31 | "memchr", 32 | "pin-project-lite", 33 | "tokio", 34 | "tokio-util", 35 | "tracing", 36 | ] 37 | 38 | [[package]] 39 | name = "actix-http" 40 | version = "3.6.0" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "d223b13fd481fc0d1f83bb12659ae774d9e3601814c68a0bc539731698cca743" 43 | dependencies = [ 44 | "actix-codec", 45 | "actix-rt", 46 | "actix-service", 47 | "actix-utils", 48 | "ahash", 49 | "base64 0.21.0", 50 | "bitflags 2.4.0", 51 | "brotli", 52 | "bytes", 53 | "bytestring", 54 | "derive_more", 55 | "encoding_rs", 56 | "flate2", 57 | "futures-core", 58 | "h2", 59 | "http 0.2.9", 60 | "httparse", 61 | "httpdate", 62 | "itoa", 63 | "language-tags", 64 | "local-channel", 65 | "mime", 66 | "percent-encoding", 67 | "pin-project-lite", 68 | "rand 0.8.5", 69 | "sha1 0.10.5", 70 | "smallvec", 71 | "tokio", 72 | "tokio-util", 73 | "tracing", 74 | "zstd", 75 | ] 76 | 77 | [[package]] 78 | name = "actix-macros" 79 | version = "0.2.3" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" 82 | dependencies = [ 83 | "quote", 84 | "syn 1.0.104", 85 | ] 86 | 87 | [[package]] 88 | name = "actix-rt" 89 | version = "2.9.0" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "28f32d40287d3f402ae0028a9d54bef51af15c8769492826a69d28f81893151d" 92 | dependencies = [ 93 | "actix-macros", 94 | "futures-core", 95 | "tokio", 96 | ] 97 | 98 | [[package]] 99 | name = "actix-service" 100 | version = "2.0.2" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" 103 | dependencies = [ 104 | "futures-core", 105 | "paste", 106 | "pin-project-lite", 107 | ] 108 | 109 | [[package]] 110 | name = "actix-tls" 111 | version = "3.3.0" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "d4cce60a2f2b477bc72e5cde0af1812a6e82d8fd85b5570a5dcf2a5bf2c5be5f" 114 | dependencies = [ 115 | "actix-rt", 116 | "actix-service", 117 | "actix-utils", 118 | "futures-core", 119 | "http 0.2.9", 120 | "http 1.0.0", 121 | "impl-more", 122 | "openssl", 123 | "pin-project-lite", 124 | "tokio", 125 | "tokio-openssl", 126 | "tokio-rustls 0.23.2", 127 | "tokio-util", 128 | "tracing", 129 | "webpki-roots 0.22.2", 130 | ] 131 | 132 | [[package]] 133 | name = "actix-utils" 134 | version = "3.0.0" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "e491cbaac2e7fc788dfff99ff48ef317e23b3cf63dbaf7aaab6418f40f92aa94" 137 | dependencies = [ 138 | "local-waker", 139 | "pin-project-lite", 140 | ] 141 | 142 | [[package]] 143 | name = "addr2line" 144 | version = "0.19.0" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" 147 | dependencies = [ 148 | "gimli", 149 | ] 150 | 151 | [[package]] 152 | name = "adler" 153 | version = "1.0.2" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 156 | 157 | [[package]] 158 | name = "aead" 159 | version = "0.3.2" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" 162 | dependencies = [ 163 | "generic-array", 164 | ] 165 | 166 | [[package]] 167 | name = "aes" 168 | version = "0.6.0" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" 171 | dependencies = [ 172 | "aes-soft", 173 | "aesni", 174 | "cipher", 175 | ] 176 | 177 | [[package]] 178 | name = "aes-gcm" 179 | version = "0.8.0" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da" 182 | dependencies = [ 183 | "aead", 184 | "aes", 185 | "cipher", 186 | "ctr", 187 | "ghash", 188 | "subtle", 189 | ] 190 | 191 | [[package]] 192 | name = "aes-soft" 193 | version = "0.6.4" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" 196 | dependencies = [ 197 | "cipher", 198 | "opaque-debug", 199 | ] 200 | 201 | [[package]] 202 | name = "aesni" 203 | version = "0.10.0" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" 206 | dependencies = [ 207 | "cipher", 208 | "opaque-debug", 209 | ] 210 | 211 | [[package]] 212 | name = "ahash" 213 | version = "0.8.3" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" 216 | dependencies = [ 217 | "cfg-if", 218 | "getrandom 0.2.10", 219 | "once_cell", 220 | "version_check", 221 | ] 222 | 223 | [[package]] 224 | name = "aho-corasick" 225 | version = "0.7.18" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 228 | dependencies = [ 229 | "memchr", 230 | ] 231 | 232 | [[package]] 233 | name = "alloc-no-stdlib" 234 | version = "2.0.3" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "35ef4730490ad1c4eae5c4325b2a95f521d023e5c885853ff7aca0a6a1631db3" 237 | 238 | [[package]] 239 | name = "alloc-stdlib" 240 | version = "0.2.1" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "697ed7edc0f1711de49ce108c541623a0af97c6c60b2f6e2b65229847ac843c2" 243 | dependencies = [ 244 | "alloc-no-stdlib", 245 | ] 246 | 247 | [[package]] 248 | name = "anyhow" 249 | version = "1.0.56" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" 252 | 253 | [[package]] 254 | name = "arrayvec" 255 | version = "0.5.2" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" 258 | 259 | [[package]] 260 | name = "async-attributes" 261 | version = "1.1.2" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" 264 | dependencies = [ 265 | "quote", 266 | "syn 1.0.104", 267 | ] 268 | 269 | [[package]] 270 | name = "async-channel" 271 | version = "1.6.1" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" 274 | dependencies = [ 275 | "concurrent-queue", 276 | "event-listener", 277 | "futures-core", 278 | ] 279 | 280 | [[package]] 281 | name = "async-dup" 282 | version = "1.2.2" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | checksum = "7427a12b8dc09291528cfb1da2447059adb4a257388c2acd6497a79d55cf6f7c" 285 | dependencies = [ 286 | "futures-io", 287 | "simple-mutex", 288 | ] 289 | 290 | [[package]] 291 | name = "async-executor" 292 | version = "1.4.1" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" 295 | dependencies = [ 296 | "async-task", 297 | "concurrent-queue", 298 | "fastrand", 299 | "futures-lite", 300 | "once_cell", 301 | "slab", 302 | ] 303 | 304 | [[package]] 305 | name = "async-global-executor" 306 | version = "2.0.3" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "c026b7e44f1316b567ee750fea85103f87fcb80792b860e979f221259796ca0a" 309 | dependencies = [ 310 | "async-channel", 311 | "async-executor", 312 | "async-io", 313 | "async-mutex", 314 | "blocking", 315 | "futures-lite", 316 | "num_cpus", 317 | "once_cell", 318 | ] 319 | 320 | [[package]] 321 | name = "async-h1" 322 | version = "2.3.3" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "8101020758a4fc3a7c326cb42aa99e9fa77cbfb76987c128ad956406fe1f70a7" 325 | dependencies = [ 326 | "async-channel", 327 | "async-dup", 328 | "async-std", 329 | "futures-core", 330 | "http-types", 331 | "httparse", 332 | "log", 333 | "pin-project", 334 | ] 335 | 336 | [[package]] 337 | name = "async-io" 338 | version = "1.6.0" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "a811e6a479f2439f0c04038796b5cfb3d2ad56c230e0f2d3f7b04d68cfee607b" 341 | dependencies = [ 342 | "concurrent-queue", 343 | "futures-lite", 344 | "libc", 345 | "log", 346 | "once_cell", 347 | "parking", 348 | "polling", 349 | "slab", 350 | "socket2 0.4.9", 351 | "waker-fn", 352 | "winapi", 353 | ] 354 | 355 | [[package]] 356 | name = "async-lock" 357 | version = "2.5.0" 358 | source = "registry+https://github.com/rust-lang/crates.io-index" 359 | checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6" 360 | dependencies = [ 361 | "event-listener", 362 | ] 363 | 364 | [[package]] 365 | name = "async-mutex" 366 | version = "1.4.0" 367 | source = "registry+https://github.com/rust-lang/crates.io-index" 368 | checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" 369 | dependencies = [ 370 | "event-listener", 371 | ] 372 | 373 | [[package]] 374 | name = "async-native-tls" 375 | version = "0.3.3" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "9e9e7a929bd34c68a82d58a4de7f86fffdaf97fb2af850162a7bb19dd7269b33" 378 | dependencies = [ 379 | "async-std", 380 | "native-tls", 381 | "thiserror", 382 | "url", 383 | ] 384 | 385 | [[package]] 386 | name = "async-native-tls" 387 | version = "0.5.0" 388 | source = "registry+https://github.com/rust-lang/crates.io-index" 389 | checksum = "9343dc5acf07e79ff82d0c37899f079db3534d99f189a1837c8e549c99405bec" 390 | dependencies = [ 391 | "futures-util", 392 | "native-tls", 393 | "thiserror", 394 | "url", 395 | ] 396 | 397 | [[package]] 398 | name = "async-std" 399 | version = "1.12.0" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" 402 | dependencies = [ 403 | "async-attributes", 404 | "async-channel", 405 | "async-global-executor", 406 | "async-io", 407 | "async-lock", 408 | "crossbeam-utils", 409 | "futures-channel", 410 | "futures-core", 411 | "futures-io", 412 | "futures-lite", 413 | "gloo-timers", 414 | "kv-log-macro", 415 | "log", 416 | "memchr", 417 | "once_cell", 418 | "pin-project-lite", 419 | "pin-utils", 420 | "slab", 421 | "wasm-bindgen-futures", 422 | ] 423 | 424 | [[package]] 425 | name = "async-stream" 426 | version = "0.3.5" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" 429 | dependencies = [ 430 | "async-stream-impl", 431 | "futures-core", 432 | "pin-project-lite", 433 | ] 434 | 435 | [[package]] 436 | name = "async-stream-impl" 437 | version = "0.3.5" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" 440 | dependencies = [ 441 | "proc-macro2", 442 | "quote", 443 | "syn 2.0.46", 444 | ] 445 | 446 | [[package]] 447 | name = "async-task" 448 | version = "4.2.0" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9" 451 | 452 | [[package]] 453 | name = "async-trait" 454 | version = "0.1.52" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3" 457 | dependencies = [ 458 | "proc-macro2", 459 | "quote", 460 | "syn 1.0.104", 461 | ] 462 | 463 | [[package]] 464 | name = "async-tungstenite" 465 | version = "0.23.0" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "a1e9efbe14612da0a19fb983059a0b621e9cf6225d7018ecab4f9988215540dc" 468 | dependencies = [ 469 | "async-native-tls 0.5.0", 470 | "async-std", 471 | "futures-io", 472 | "futures-util", 473 | "log", 474 | "native-tls", 475 | "pin-project-lite", 476 | "rustls-native-certs", 477 | "tokio", 478 | "tokio-native-tls", 479 | "tokio-rustls 0.24.0", 480 | "tungstenite", 481 | "webpki-roots 0.25.2", 482 | ] 483 | 484 | [[package]] 485 | name = "atomic-waker" 486 | version = "1.0.0" 487 | source = "registry+https://github.com/rust-lang/crates.io-index" 488 | checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" 489 | 490 | [[package]] 491 | name = "autocfg" 492 | version = "1.1.0" 493 | source = "registry+https://github.com/rust-lang/crates.io-index" 494 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 495 | 496 | [[package]] 497 | name = "awc" 498 | version = "3.4.0" 499 | source = "registry+https://github.com/rust-lang/crates.io-index" 500 | checksum = "68c09cc97310b926f01621faee652f3d1b0962545a3cec6c9ac07def9ea36c2c" 501 | dependencies = [ 502 | "actix-codec", 503 | "actix-http", 504 | "actix-rt", 505 | "actix-service", 506 | "actix-tls", 507 | "actix-utils", 508 | "base64 0.21.0", 509 | "bytes", 510 | "cfg-if", 511 | "cookie 0.16.0", 512 | "derive_more", 513 | "futures-core", 514 | "futures-util", 515 | "h2", 516 | "http 0.2.9", 517 | "itoa", 518 | "log", 519 | "mime", 520 | "openssl", 521 | "percent-encoding", 522 | "pin-project-lite", 523 | "rand 0.8.5", 524 | "rustls 0.20.4", 525 | "serde", 526 | "serde_json", 527 | "serde_urlencoded", 528 | "tokio", 529 | ] 530 | 531 | [[package]] 532 | name = "backtrace" 533 | version = "0.3.67" 534 | source = "registry+https://github.com/rust-lang/crates.io-index" 535 | checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" 536 | dependencies = [ 537 | "addr2line", 538 | "cc", 539 | "cfg-if", 540 | "libc", 541 | "miniz_oxide 0.6.2", 542 | "object", 543 | "rustc-demangle", 544 | ] 545 | 546 | [[package]] 547 | name = "base-x" 548 | version = "0.2.8" 549 | source = "registry+https://github.com/rust-lang/crates.io-index" 550 | checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" 551 | 552 | [[package]] 553 | name = "base64" 554 | version = "0.13.0" 555 | source = "registry+https://github.com/rust-lang/crates.io-index" 556 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 557 | 558 | [[package]] 559 | name = "base64" 560 | version = "0.21.0" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" 563 | 564 | [[package]] 565 | name = "bililive" 566 | version = "0.2.0-beta.5" 567 | dependencies = [ 568 | "async-std", 569 | "async-tungstenite", 570 | "bililive-core", 571 | "futures", 572 | "http-client", 573 | "log", 574 | "pretty_env_logger", 575 | "reqwest", 576 | "serde", 577 | "serde_json", 578 | "stream-reconnect", 579 | "thiserror", 580 | "tokio", 581 | "tokio-test", 582 | "url", 583 | ] 584 | 585 | [[package]] 586 | name = "bililive-core" 587 | version = "0.1.0-beta.4" 588 | dependencies = [ 589 | "async-std", 590 | "flate2", 591 | "futures", 592 | "log", 593 | "nom 7.1.3", 594 | "rand 0.8.5", 595 | "serde", 596 | "serde_json", 597 | "stream-reconnect", 598 | "thiserror", 599 | "tokio", 600 | "url", 601 | ] 602 | 603 | [[package]] 604 | name = "bitflags" 605 | version = "1.3.2" 606 | source = "registry+https://github.com/rust-lang/crates.io-index" 607 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 608 | 609 | [[package]] 610 | name = "bitflags" 611 | version = "2.4.0" 612 | source = "registry+https://github.com/rust-lang/crates.io-index" 613 | checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" 614 | 615 | [[package]] 616 | name = "block-buffer" 617 | version = "0.9.0" 618 | source = "registry+https://github.com/rust-lang/crates.io-index" 619 | checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" 620 | dependencies = [ 621 | "generic-array", 622 | ] 623 | 624 | [[package]] 625 | name = "block-buffer" 626 | version = "0.10.2" 627 | source = "registry+https://github.com/rust-lang/crates.io-index" 628 | checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" 629 | dependencies = [ 630 | "generic-array", 631 | ] 632 | 633 | [[package]] 634 | name = "blocking" 635 | version = "1.1.0" 636 | source = "registry+https://github.com/rust-lang/crates.io-index" 637 | checksum = "046e47d4b2d391b1f6f8b407b1deb8dee56c1852ccd868becf2710f601b5f427" 638 | dependencies = [ 639 | "async-channel", 640 | "async-task", 641 | "atomic-waker", 642 | "fastrand", 643 | "futures-lite", 644 | "once_cell", 645 | ] 646 | 647 | [[package]] 648 | name = "brotli" 649 | version = "3.3.3" 650 | source = "registry+https://github.com/rust-lang/crates.io-index" 651 | checksum = "f838e47a451d5a8fa552371f80024dd6ace9b7acdf25c4c3d0f9bc6816fb1c39" 652 | dependencies = [ 653 | "alloc-no-stdlib", 654 | "alloc-stdlib", 655 | "brotli-decompressor", 656 | ] 657 | 658 | [[package]] 659 | name = "brotli-decompressor" 660 | version = "2.3.2" 661 | source = "registry+https://github.com/rust-lang/crates.io-index" 662 | checksum = "59ad2d4653bf5ca36ae797b1f4bb4dbddb60ce49ca4aed8a2ce4829f60425b80" 663 | dependencies = [ 664 | "alloc-no-stdlib", 665 | "alloc-stdlib", 666 | ] 667 | 668 | [[package]] 669 | name = "bumpalo" 670 | version = "3.12.0" 671 | source = "registry+https://github.com/rust-lang/crates.io-index" 672 | checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" 673 | 674 | [[package]] 675 | name = "byteorder" 676 | version = "1.4.3" 677 | source = "registry+https://github.com/rust-lang/crates.io-index" 678 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 679 | 680 | [[package]] 681 | name = "bytes" 682 | version = "1.5.0" 683 | source = "registry+https://github.com/rust-lang/crates.io-index" 684 | checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" 685 | 686 | [[package]] 687 | name = "bytestring" 688 | version = "1.0.0" 689 | source = "registry+https://github.com/rust-lang/crates.io-index" 690 | checksum = "90706ba19e97b90786e19dc0d5e2abd80008d99d4c0c5d1ad0b5e72cec7c494d" 691 | dependencies = [ 692 | "bytes", 693 | ] 694 | 695 | [[package]] 696 | name = "cache-padded" 697 | version = "1.2.0" 698 | source = "registry+https://github.com/rust-lang/crates.io-index" 699 | checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" 700 | 701 | [[package]] 702 | name = "cc" 703 | version = "1.0.73" 704 | source = "registry+https://github.com/rust-lang/crates.io-index" 705 | checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" 706 | dependencies = [ 707 | "jobserver", 708 | ] 709 | 710 | [[package]] 711 | name = "cfg-if" 712 | version = "1.0.0" 713 | source = "registry+https://github.com/rust-lang/crates.io-index" 714 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 715 | 716 | [[package]] 717 | name = "cipher" 718 | version = "0.2.5" 719 | source = "registry+https://github.com/rust-lang/crates.io-index" 720 | checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" 721 | dependencies = [ 722 | "generic-array", 723 | ] 724 | 725 | [[package]] 726 | name = "concurrent-queue" 727 | version = "1.2.2" 728 | source = "registry+https://github.com/rust-lang/crates.io-index" 729 | checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" 730 | dependencies = [ 731 | "cache-padded", 732 | ] 733 | 734 | [[package]] 735 | name = "config" 736 | version = "0.10.1" 737 | source = "registry+https://github.com/rust-lang/crates.io-index" 738 | checksum = "19b076e143e1d9538dde65da30f8481c2a6c44040edb8e02b9bf1351edb92ce3" 739 | dependencies = [ 740 | "lazy_static", 741 | "nom 5.1.2", 742 | "serde", 743 | ] 744 | 745 | [[package]] 746 | name = "const_fn" 747 | version = "0.4.9" 748 | source = "registry+https://github.com/rust-lang/crates.io-index" 749 | checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" 750 | 751 | [[package]] 752 | name = "convert_case" 753 | version = "0.4.0" 754 | source = "registry+https://github.com/rust-lang/crates.io-index" 755 | checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" 756 | 757 | [[package]] 758 | name = "cookie" 759 | version = "0.14.4" 760 | source = "registry+https://github.com/rust-lang/crates.io-index" 761 | checksum = "03a5d7b21829bc7b4bf4754a978a241ae54ea55a40f92bb20216e54096f4b951" 762 | dependencies = [ 763 | "aes-gcm", 764 | "base64 0.13.0", 765 | "hkdf", 766 | "hmac", 767 | "percent-encoding", 768 | "rand 0.8.5", 769 | "sha2", 770 | "time 0.2.27", 771 | "version_check", 772 | ] 773 | 774 | [[package]] 775 | name = "cookie" 776 | version = "0.16.0" 777 | source = "registry+https://github.com/rust-lang/crates.io-index" 778 | checksum = "94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05" 779 | dependencies = [ 780 | "percent-encoding", 781 | "time 0.3.7", 782 | "version_check", 783 | ] 784 | 785 | [[package]] 786 | name = "core-foundation" 787 | version = "0.9.3" 788 | source = "registry+https://github.com/rust-lang/crates.io-index" 789 | checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" 790 | dependencies = [ 791 | "core-foundation-sys", 792 | "libc", 793 | ] 794 | 795 | [[package]] 796 | name = "core-foundation-sys" 797 | version = "0.8.3" 798 | source = "registry+https://github.com/rust-lang/crates.io-index" 799 | checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" 800 | 801 | [[package]] 802 | name = "cpufeatures" 803 | version = "0.2.1" 804 | source = "registry+https://github.com/rust-lang/crates.io-index" 805 | checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" 806 | dependencies = [ 807 | "libc", 808 | ] 809 | 810 | [[package]] 811 | name = "cpuid-bool" 812 | version = "0.2.0" 813 | source = "registry+https://github.com/rust-lang/crates.io-index" 814 | checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" 815 | 816 | [[package]] 817 | name = "crc32fast" 818 | version = "1.3.2" 819 | source = "registry+https://github.com/rust-lang/crates.io-index" 820 | checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" 821 | dependencies = [ 822 | "cfg-if", 823 | ] 824 | 825 | [[package]] 826 | name = "crossbeam-queue" 827 | version = "0.3.4" 828 | source = "registry+https://github.com/rust-lang/crates.io-index" 829 | checksum = "4dd435b205a4842da59efd07628f921c096bc1cc0a156835b4fa0bcb9a19bcce" 830 | dependencies = [ 831 | "cfg-if", 832 | "crossbeam-utils", 833 | ] 834 | 835 | [[package]] 836 | name = "crossbeam-utils" 837 | version = "0.8.7" 838 | source = "registry+https://github.com/rust-lang/crates.io-index" 839 | checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6" 840 | dependencies = [ 841 | "cfg-if", 842 | "lazy_static", 843 | ] 844 | 845 | [[package]] 846 | name = "crypto-common" 847 | version = "0.1.3" 848 | source = "registry+https://github.com/rust-lang/crates.io-index" 849 | checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" 850 | dependencies = [ 851 | "generic-array", 852 | "typenum", 853 | ] 854 | 855 | [[package]] 856 | name = "crypto-mac" 857 | version = "0.10.1" 858 | source = "registry+https://github.com/rust-lang/crates.io-index" 859 | checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" 860 | dependencies = [ 861 | "generic-array", 862 | "subtle", 863 | ] 864 | 865 | [[package]] 866 | name = "ctr" 867 | version = "0.6.0" 868 | source = "registry+https://github.com/rust-lang/crates.io-index" 869 | checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f" 870 | dependencies = [ 871 | "cipher", 872 | ] 873 | 874 | [[package]] 875 | name = "dashmap" 876 | version = "5.3.4" 877 | source = "registry+https://github.com/rust-lang/crates.io-index" 878 | checksum = "3495912c9c1ccf2e18976439f4443f3fee0fd61f424ff99fde6a66b15ecb448f" 879 | dependencies = [ 880 | "cfg-if", 881 | "hashbrown 0.12.1", 882 | "lock_api", 883 | "parking_lot_core", 884 | ] 885 | 886 | [[package]] 887 | name = "data-encoding" 888 | version = "2.3.3" 889 | source = "registry+https://github.com/rust-lang/crates.io-index" 890 | checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" 891 | 892 | [[package]] 893 | name = "deadpool" 894 | version = "0.7.0" 895 | source = "registry+https://github.com/rust-lang/crates.io-index" 896 | checksum = "3d126179d86aee4556e54f5f3c6bf6d9884e7cc52cef82f77ee6f90a7747616d" 897 | dependencies = [ 898 | "async-trait", 899 | "config", 900 | "crossbeam-queue", 901 | "num_cpus", 902 | "serde", 903 | "tokio", 904 | ] 905 | 906 | [[package]] 907 | name = "derive_more" 908 | version = "0.99.17" 909 | source = "registry+https://github.com/rust-lang/crates.io-index" 910 | checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" 911 | dependencies = [ 912 | "convert_case", 913 | "proc-macro2", 914 | "quote", 915 | "rustc_version 0.4.0", 916 | "syn 1.0.104", 917 | ] 918 | 919 | [[package]] 920 | name = "digest" 921 | version = "0.9.0" 922 | source = "registry+https://github.com/rust-lang/crates.io-index" 923 | checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" 924 | dependencies = [ 925 | "generic-array", 926 | ] 927 | 928 | [[package]] 929 | name = "digest" 930 | version = "0.10.6" 931 | source = "registry+https://github.com/rust-lang/crates.io-index" 932 | checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" 933 | dependencies = [ 934 | "block-buffer 0.10.2", 935 | "crypto-common", 936 | ] 937 | 938 | [[package]] 939 | name = "discard" 940 | version = "1.0.4" 941 | source = "registry+https://github.com/rust-lang/crates.io-index" 942 | checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" 943 | 944 | [[package]] 945 | name = "encoding_rs" 946 | version = "0.8.30" 947 | source = "registry+https://github.com/rust-lang/crates.io-index" 948 | checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df" 949 | dependencies = [ 950 | "cfg-if", 951 | ] 952 | 953 | [[package]] 954 | name = "env_logger" 955 | version = "0.10.0" 956 | source = "registry+https://github.com/rust-lang/crates.io-index" 957 | checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" 958 | dependencies = [ 959 | "humantime", 960 | "is-terminal", 961 | "log", 962 | "regex", 963 | "termcolor", 964 | ] 965 | 966 | [[package]] 967 | name = "equivalent" 968 | version = "1.0.1" 969 | source = "registry+https://github.com/rust-lang/crates.io-index" 970 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 971 | 972 | [[package]] 973 | name = "errno" 974 | version = "0.3.1" 975 | source = "registry+https://github.com/rust-lang/crates.io-index" 976 | checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" 977 | dependencies = [ 978 | "errno-dragonfly", 979 | "libc", 980 | "windows-sys 0.48.0", 981 | ] 982 | 983 | [[package]] 984 | name = "errno-dragonfly" 985 | version = "0.1.2" 986 | source = "registry+https://github.com/rust-lang/crates.io-index" 987 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" 988 | dependencies = [ 989 | "cc", 990 | "libc", 991 | ] 992 | 993 | [[package]] 994 | name = "event-listener" 995 | version = "2.5.2" 996 | source = "registry+https://github.com/rust-lang/crates.io-index" 997 | checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" 998 | 999 | [[package]] 1000 | name = "fastrand" 1001 | version = "1.7.0" 1002 | source = "registry+https://github.com/rust-lang/crates.io-index" 1003 | checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" 1004 | dependencies = [ 1005 | "instant", 1006 | ] 1007 | 1008 | [[package]] 1009 | name = "flate2" 1010 | version = "1.0.28" 1011 | source = "registry+https://github.com/rust-lang/crates.io-index" 1012 | checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" 1013 | dependencies = [ 1014 | "crc32fast", 1015 | "miniz_oxide 0.7.1", 1016 | ] 1017 | 1018 | [[package]] 1019 | name = "fnv" 1020 | version = "1.0.7" 1021 | source = "registry+https://github.com/rust-lang/crates.io-index" 1022 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 1023 | 1024 | [[package]] 1025 | name = "foreign-types" 1026 | version = "0.3.2" 1027 | source = "registry+https://github.com/rust-lang/crates.io-index" 1028 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 1029 | dependencies = [ 1030 | "foreign-types-shared", 1031 | ] 1032 | 1033 | [[package]] 1034 | name = "foreign-types-shared" 1035 | version = "0.1.1" 1036 | source = "registry+https://github.com/rust-lang/crates.io-index" 1037 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 1038 | 1039 | [[package]] 1040 | name = "form_urlencoded" 1041 | version = "1.2.1" 1042 | source = "registry+https://github.com/rust-lang/crates.io-index" 1043 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 1044 | dependencies = [ 1045 | "percent-encoding", 1046 | ] 1047 | 1048 | [[package]] 1049 | name = "futures" 1050 | version = "0.3.30" 1051 | source = "registry+https://github.com/rust-lang/crates.io-index" 1052 | checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" 1053 | dependencies = [ 1054 | "futures-channel", 1055 | "futures-core", 1056 | "futures-executor", 1057 | "futures-io", 1058 | "futures-sink", 1059 | "futures-task", 1060 | "futures-util", 1061 | ] 1062 | 1063 | [[package]] 1064 | name = "futures-channel" 1065 | version = "0.3.30" 1066 | source = "registry+https://github.com/rust-lang/crates.io-index" 1067 | checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" 1068 | dependencies = [ 1069 | "futures-core", 1070 | "futures-sink", 1071 | ] 1072 | 1073 | [[package]] 1074 | name = "futures-core" 1075 | version = "0.3.30" 1076 | source = "registry+https://github.com/rust-lang/crates.io-index" 1077 | checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" 1078 | 1079 | [[package]] 1080 | name = "futures-executor" 1081 | version = "0.3.30" 1082 | source = "registry+https://github.com/rust-lang/crates.io-index" 1083 | checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" 1084 | dependencies = [ 1085 | "futures-core", 1086 | "futures-task", 1087 | "futures-util", 1088 | ] 1089 | 1090 | [[package]] 1091 | name = "futures-io" 1092 | version = "0.3.30" 1093 | source = "registry+https://github.com/rust-lang/crates.io-index" 1094 | checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" 1095 | 1096 | [[package]] 1097 | name = "futures-lite" 1098 | version = "1.12.0" 1099 | source = "registry+https://github.com/rust-lang/crates.io-index" 1100 | checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" 1101 | dependencies = [ 1102 | "fastrand", 1103 | "futures-core", 1104 | "futures-io", 1105 | "memchr", 1106 | "parking", 1107 | "pin-project-lite", 1108 | "waker-fn", 1109 | ] 1110 | 1111 | [[package]] 1112 | name = "futures-macro" 1113 | version = "0.3.30" 1114 | source = "registry+https://github.com/rust-lang/crates.io-index" 1115 | checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" 1116 | dependencies = [ 1117 | "proc-macro2", 1118 | "quote", 1119 | "syn 2.0.46", 1120 | ] 1121 | 1122 | [[package]] 1123 | name = "futures-sink" 1124 | version = "0.3.30" 1125 | source = "registry+https://github.com/rust-lang/crates.io-index" 1126 | checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" 1127 | 1128 | [[package]] 1129 | name = "futures-task" 1130 | version = "0.3.30" 1131 | source = "registry+https://github.com/rust-lang/crates.io-index" 1132 | checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" 1133 | 1134 | [[package]] 1135 | name = "futures-util" 1136 | version = "0.3.30" 1137 | source = "registry+https://github.com/rust-lang/crates.io-index" 1138 | checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" 1139 | dependencies = [ 1140 | "futures-channel", 1141 | "futures-core", 1142 | "futures-io", 1143 | "futures-macro", 1144 | "futures-sink", 1145 | "futures-task", 1146 | "memchr", 1147 | "pin-project-lite", 1148 | "pin-utils", 1149 | "slab", 1150 | ] 1151 | 1152 | [[package]] 1153 | name = "generic-array" 1154 | version = "0.14.5" 1155 | source = "registry+https://github.com/rust-lang/crates.io-index" 1156 | checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" 1157 | dependencies = [ 1158 | "typenum", 1159 | "version_check", 1160 | ] 1161 | 1162 | [[package]] 1163 | name = "getrandom" 1164 | version = "0.1.16" 1165 | source = "registry+https://github.com/rust-lang/crates.io-index" 1166 | checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" 1167 | dependencies = [ 1168 | "cfg-if", 1169 | "libc", 1170 | "wasi 0.9.0+wasi-snapshot-preview1", 1171 | ] 1172 | 1173 | [[package]] 1174 | name = "getrandom" 1175 | version = "0.2.10" 1176 | source = "registry+https://github.com/rust-lang/crates.io-index" 1177 | checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" 1178 | dependencies = [ 1179 | "cfg-if", 1180 | "libc", 1181 | "wasi 0.11.0+wasi-snapshot-preview1", 1182 | ] 1183 | 1184 | [[package]] 1185 | name = "ghash" 1186 | version = "0.3.1" 1187 | source = "registry+https://github.com/rust-lang/crates.io-index" 1188 | checksum = "97304e4cd182c3846f7575ced3890c53012ce534ad9114046b0a9e00bb30a375" 1189 | dependencies = [ 1190 | "opaque-debug", 1191 | "polyval", 1192 | ] 1193 | 1194 | [[package]] 1195 | name = "gimli" 1196 | version = "0.27.3" 1197 | source = "registry+https://github.com/rust-lang/crates.io-index" 1198 | checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" 1199 | 1200 | [[package]] 1201 | name = "gloo-timers" 1202 | version = "0.2.3" 1203 | source = "registry+https://github.com/rust-lang/crates.io-index" 1204 | checksum = "4d12a7f4e95cfe710f1d624fb1210b7d961a5fb05c4fd942f4feab06e61f590e" 1205 | dependencies = [ 1206 | "futures-channel", 1207 | "futures-core", 1208 | "js-sys", 1209 | "wasm-bindgen", 1210 | ] 1211 | 1212 | [[package]] 1213 | name = "h2" 1214 | version = "0.3.24" 1215 | source = "registry+https://github.com/rust-lang/crates.io-index" 1216 | checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" 1217 | dependencies = [ 1218 | "bytes", 1219 | "fnv", 1220 | "futures-core", 1221 | "futures-sink", 1222 | "futures-util", 1223 | "http 0.2.9", 1224 | "indexmap", 1225 | "slab", 1226 | "tokio", 1227 | "tokio-util", 1228 | "tracing", 1229 | ] 1230 | 1231 | [[package]] 1232 | name = "hashbrown" 1233 | version = "0.12.1" 1234 | source = "registry+https://github.com/rust-lang/crates.io-index" 1235 | checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" 1236 | 1237 | [[package]] 1238 | name = "hashbrown" 1239 | version = "0.14.3" 1240 | source = "registry+https://github.com/rust-lang/crates.io-index" 1241 | checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" 1242 | 1243 | [[package]] 1244 | name = "hermit-abi" 1245 | version = "0.1.19" 1246 | source = "registry+https://github.com/rust-lang/crates.io-index" 1247 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 1248 | dependencies = [ 1249 | "libc", 1250 | ] 1251 | 1252 | [[package]] 1253 | name = "hermit-abi" 1254 | version = "0.3.1" 1255 | source = "registry+https://github.com/rust-lang/crates.io-index" 1256 | checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" 1257 | 1258 | [[package]] 1259 | name = "hkdf" 1260 | version = "0.10.0" 1261 | source = "registry+https://github.com/rust-lang/crates.io-index" 1262 | checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f" 1263 | dependencies = [ 1264 | "digest 0.9.0", 1265 | "hmac", 1266 | ] 1267 | 1268 | [[package]] 1269 | name = "hmac" 1270 | version = "0.10.1" 1271 | source = "registry+https://github.com/rust-lang/crates.io-index" 1272 | checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" 1273 | dependencies = [ 1274 | "crypto-mac", 1275 | "digest 0.9.0", 1276 | ] 1277 | 1278 | [[package]] 1279 | name = "http" 1280 | version = "0.2.9" 1281 | source = "registry+https://github.com/rust-lang/crates.io-index" 1282 | checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" 1283 | dependencies = [ 1284 | "bytes", 1285 | "fnv", 1286 | "itoa", 1287 | ] 1288 | 1289 | [[package]] 1290 | name = "http" 1291 | version = "1.0.0" 1292 | source = "registry+https://github.com/rust-lang/crates.io-index" 1293 | checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" 1294 | dependencies = [ 1295 | "bytes", 1296 | "fnv", 1297 | "itoa", 1298 | ] 1299 | 1300 | [[package]] 1301 | name = "http-body" 1302 | version = "0.4.4" 1303 | source = "registry+https://github.com/rust-lang/crates.io-index" 1304 | checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" 1305 | dependencies = [ 1306 | "bytes", 1307 | "http 0.2.9", 1308 | "pin-project-lite", 1309 | ] 1310 | 1311 | [[package]] 1312 | name = "http-client" 1313 | version = "6.5.3" 1314 | source = "registry+https://github.com/rust-lang/crates.io-index" 1315 | checksum = "1947510dc91e2bf586ea5ffb412caad7673264e14bb39fb9078da114a94ce1a5" 1316 | dependencies = [ 1317 | "async-h1", 1318 | "async-native-tls 0.3.3", 1319 | "async-std", 1320 | "async-trait", 1321 | "cfg-if", 1322 | "dashmap", 1323 | "deadpool", 1324 | "futures", 1325 | "http-types", 1326 | "log", 1327 | ] 1328 | 1329 | [[package]] 1330 | name = "http-types" 1331 | version = "2.12.0" 1332 | source = "registry+https://github.com/rust-lang/crates.io-index" 1333 | checksum = "6e9b187a72d63adbfba487f48095306ac823049cb504ee195541e91c7775f5ad" 1334 | dependencies = [ 1335 | "anyhow", 1336 | "async-channel", 1337 | "async-std", 1338 | "base64 0.13.0", 1339 | "cookie 0.14.4", 1340 | "futures-lite", 1341 | "infer", 1342 | "pin-project-lite", 1343 | "rand 0.7.3", 1344 | "serde", 1345 | "serde_json", 1346 | "serde_qs", 1347 | "serde_urlencoded", 1348 | "url", 1349 | ] 1350 | 1351 | [[package]] 1352 | name = "httparse" 1353 | version = "1.8.0" 1354 | source = "registry+https://github.com/rust-lang/crates.io-index" 1355 | checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" 1356 | 1357 | [[package]] 1358 | name = "httpdate" 1359 | version = "1.0.2" 1360 | source = "registry+https://github.com/rust-lang/crates.io-index" 1361 | checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" 1362 | 1363 | [[package]] 1364 | name = "humantime" 1365 | version = "2.1.0" 1366 | source = "registry+https://github.com/rust-lang/crates.io-index" 1367 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 1368 | 1369 | [[package]] 1370 | name = "hyper" 1371 | version = "0.14.27" 1372 | source = "registry+https://github.com/rust-lang/crates.io-index" 1373 | checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" 1374 | dependencies = [ 1375 | "bytes", 1376 | "futures-channel", 1377 | "futures-core", 1378 | "futures-util", 1379 | "h2", 1380 | "http 0.2.9", 1381 | "http-body", 1382 | "httparse", 1383 | "httpdate", 1384 | "itoa", 1385 | "pin-project-lite", 1386 | "socket2 0.4.9", 1387 | "tokio", 1388 | "tower-service", 1389 | "tracing", 1390 | "want", 1391 | ] 1392 | 1393 | [[package]] 1394 | name = "hyper-rustls" 1395 | version = "0.24.0" 1396 | source = "registry+https://github.com/rust-lang/crates.io-index" 1397 | checksum = "0646026eb1b3eea4cd9ba47912ea5ce9cc07713d105b1a14698f4e6433d348b7" 1398 | dependencies = [ 1399 | "http 0.2.9", 1400 | "hyper", 1401 | "rustls 0.21.6", 1402 | "tokio", 1403 | "tokio-rustls 0.24.0", 1404 | ] 1405 | 1406 | [[package]] 1407 | name = "hyper-tls" 1408 | version = "0.5.0" 1409 | source = "registry+https://github.com/rust-lang/crates.io-index" 1410 | checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" 1411 | dependencies = [ 1412 | "bytes", 1413 | "hyper", 1414 | "native-tls", 1415 | "tokio", 1416 | "tokio-native-tls", 1417 | ] 1418 | 1419 | [[package]] 1420 | name = "idna" 1421 | version = "0.5.0" 1422 | source = "registry+https://github.com/rust-lang/crates.io-index" 1423 | checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" 1424 | dependencies = [ 1425 | "unicode-bidi", 1426 | "unicode-normalization", 1427 | ] 1428 | 1429 | [[package]] 1430 | name = "impl-more" 1431 | version = "0.1.6" 1432 | source = "registry+https://github.com/rust-lang/crates.io-index" 1433 | checksum = "206ca75c9c03ba3d4ace2460e57b189f39f43de612c2f85836e65c929701bb2d" 1434 | 1435 | [[package]] 1436 | name = "indexmap" 1437 | version = "2.1.0" 1438 | source = "registry+https://github.com/rust-lang/crates.io-index" 1439 | checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" 1440 | dependencies = [ 1441 | "equivalent", 1442 | "hashbrown 0.14.3", 1443 | ] 1444 | 1445 | [[package]] 1446 | name = "infer" 1447 | version = "0.2.3" 1448 | source = "registry+https://github.com/rust-lang/crates.io-index" 1449 | checksum = "64e9829a50b42bb782c1df523f78d332fe371b10c661e78b7a3c34b0198e9fac" 1450 | 1451 | [[package]] 1452 | name = "instant" 1453 | version = "0.1.12" 1454 | source = "registry+https://github.com/rust-lang/crates.io-index" 1455 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 1456 | dependencies = [ 1457 | "cfg-if", 1458 | ] 1459 | 1460 | [[package]] 1461 | name = "io-lifetimes" 1462 | version = "1.0.10" 1463 | source = "registry+https://github.com/rust-lang/crates.io-index" 1464 | checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" 1465 | dependencies = [ 1466 | "hermit-abi 0.3.1", 1467 | "libc", 1468 | "windows-sys 0.48.0", 1469 | ] 1470 | 1471 | [[package]] 1472 | name = "ipnet" 1473 | version = "2.4.0" 1474 | source = "registry+https://github.com/rust-lang/crates.io-index" 1475 | checksum = "35e70ee094dc02fd9c13fdad4940090f22dbd6ac7c9e7094a46cf0232a50bc7c" 1476 | 1477 | [[package]] 1478 | name = "is-terminal" 1479 | version = "0.4.7" 1480 | source = "registry+https://github.com/rust-lang/crates.io-index" 1481 | checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" 1482 | dependencies = [ 1483 | "hermit-abi 0.3.1", 1484 | "io-lifetimes", 1485 | "rustix", 1486 | "windows-sys 0.48.0", 1487 | ] 1488 | 1489 | [[package]] 1490 | name = "itoa" 1491 | version = "1.0.1" 1492 | source = "registry+https://github.com/rust-lang/crates.io-index" 1493 | checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" 1494 | 1495 | [[package]] 1496 | name = "jobserver" 1497 | version = "0.1.24" 1498 | source = "registry+https://github.com/rust-lang/crates.io-index" 1499 | checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" 1500 | dependencies = [ 1501 | "libc", 1502 | ] 1503 | 1504 | [[package]] 1505 | name = "js-sys" 1506 | version = "0.3.56" 1507 | source = "registry+https://github.com/rust-lang/crates.io-index" 1508 | checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" 1509 | dependencies = [ 1510 | "wasm-bindgen", 1511 | ] 1512 | 1513 | [[package]] 1514 | name = "kv-log-macro" 1515 | version = "1.0.7" 1516 | source = "registry+https://github.com/rust-lang/crates.io-index" 1517 | checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" 1518 | dependencies = [ 1519 | "log", 1520 | ] 1521 | 1522 | [[package]] 1523 | name = "language-tags" 1524 | version = "0.3.2" 1525 | source = "registry+https://github.com/rust-lang/crates.io-index" 1526 | checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" 1527 | 1528 | [[package]] 1529 | name = "lazy_static" 1530 | version = "1.4.0" 1531 | source = "registry+https://github.com/rust-lang/crates.io-index" 1532 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 1533 | 1534 | [[package]] 1535 | name = "lexical-core" 1536 | version = "0.7.6" 1537 | source = "registry+https://github.com/rust-lang/crates.io-index" 1538 | checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" 1539 | dependencies = [ 1540 | "arrayvec", 1541 | "bitflags 1.3.2", 1542 | "cfg-if", 1543 | "ryu", 1544 | "static_assertions", 1545 | ] 1546 | 1547 | [[package]] 1548 | name = "libc" 1549 | version = "0.2.150" 1550 | source = "registry+https://github.com/rust-lang/crates.io-index" 1551 | checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" 1552 | 1553 | [[package]] 1554 | name = "linux-raw-sys" 1555 | version = "0.3.7" 1556 | source = "registry+https://github.com/rust-lang/crates.io-index" 1557 | checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" 1558 | 1559 | [[package]] 1560 | name = "local-channel" 1561 | version = "0.1.2" 1562 | source = "registry+https://github.com/rust-lang/crates.io-index" 1563 | checksum = "6246c68cf195087205a0512559c97e15eaf95198bf0e206d662092cdcb03fe9f" 1564 | dependencies = [ 1565 | "futures-core", 1566 | "futures-sink", 1567 | "futures-util", 1568 | "local-waker", 1569 | ] 1570 | 1571 | [[package]] 1572 | name = "local-waker" 1573 | version = "0.1.2" 1574 | source = "registry+https://github.com/rust-lang/crates.io-index" 1575 | checksum = "902eb695eb0591864543cbfbf6d742510642a605a61fc5e97fe6ceb5a30ac4fb" 1576 | 1577 | [[package]] 1578 | name = "lock_api" 1579 | version = "0.4.7" 1580 | source = "registry+https://github.com/rust-lang/crates.io-index" 1581 | checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" 1582 | dependencies = [ 1583 | "autocfg", 1584 | "scopeguard", 1585 | ] 1586 | 1587 | [[package]] 1588 | name = "log" 1589 | version = "0.4.21" 1590 | source = "registry+https://github.com/rust-lang/crates.io-index" 1591 | checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" 1592 | dependencies = [ 1593 | "value-bag", 1594 | ] 1595 | 1596 | [[package]] 1597 | name = "memchr" 1598 | version = "2.4.1" 1599 | source = "registry+https://github.com/rust-lang/crates.io-index" 1600 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 1601 | 1602 | [[package]] 1603 | name = "mime" 1604 | version = "0.3.16" 1605 | source = "registry+https://github.com/rust-lang/crates.io-index" 1606 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 1607 | 1608 | [[package]] 1609 | name = "minimal-lexical" 1610 | version = "0.2.1" 1611 | source = "registry+https://github.com/rust-lang/crates.io-index" 1612 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 1613 | 1614 | [[package]] 1615 | name = "miniz_oxide" 1616 | version = "0.6.2" 1617 | source = "registry+https://github.com/rust-lang/crates.io-index" 1618 | checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" 1619 | dependencies = [ 1620 | "adler", 1621 | ] 1622 | 1623 | [[package]] 1624 | name = "miniz_oxide" 1625 | version = "0.7.1" 1626 | source = "registry+https://github.com/rust-lang/crates.io-index" 1627 | checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" 1628 | dependencies = [ 1629 | "adler", 1630 | ] 1631 | 1632 | [[package]] 1633 | name = "mio" 1634 | version = "0.8.11" 1635 | source = "registry+https://github.com/rust-lang/crates.io-index" 1636 | checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" 1637 | dependencies = [ 1638 | "libc", 1639 | "wasi 0.11.0+wasi-snapshot-preview1", 1640 | "windows-sys 0.48.0", 1641 | ] 1642 | 1643 | [[package]] 1644 | name = "native-tls" 1645 | version = "0.2.11" 1646 | source = "registry+https://github.com/rust-lang/crates.io-index" 1647 | checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" 1648 | dependencies = [ 1649 | "lazy_static", 1650 | "libc", 1651 | "log", 1652 | "openssl", 1653 | "openssl-probe", 1654 | "openssl-sys", 1655 | "schannel", 1656 | "security-framework", 1657 | "security-framework-sys", 1658 | "tempfile", 1659 | ] 1660 | 1661 | [[package]] 1662 | name = "nom" 1663 | version = "5.1.2" 1664 | source = "registry+https://github.com/rust-lang/crates.io-index" 1665 | checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" 1666 | dependencies = [ 1667 | "lexical-core", 1668 | "memchr", 1669 | "version_check", 1670 | ] 1671 | 1672 | [[package]] 1673 | name = "nom" 1674 | version = "7.1.3" 1675 | source = "registry+https://github.com/rust-lang/crates.io-index" 1676 | checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" 1677 | dependencies = [ 1678 | "memchr", 1679 | "minimal-lexical", 1680 | ] 1681 | 1682 | [[package]] 1683 | name = "num_cpus" 1684 | version = "1.13.1" 1685 | source = "registry+https://github.com/rust-lang/crates.io-index" 1686 | checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" 1687 | dependencies = [ 1688 | "hermit-abi 0.1.19", 1689 | "libc", 1690 | ] 1691 | 1692 | [[package]] 1693 | name = "num_threads" 1694 | version = "0.1.3" 1695 | source = "registry+https://github.com/rust-lang/crates.io-index" 1696 | checksum = "97ba99ba6393e2c3734791401b66902d981cb03bf190af674ca69949b6d5fb15" 1697 | dependencies = [ 1698 | "libc", 1699 | ] 1700 | 1701 | [[package]] 1702 | name = "object" 1703 | version = "0.30.4" 1704 | source = "registry+https://github.com/rust-lang/crates.io-index" 1705 | checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" 1706 | dependencies = [ 1707 | "memchr", 1708 | ] 1709 | 1710 | [[package]] 1711 | name = "once_cell" 1712 | version = "1.18.0" 1713 | source = "registry+https://github.com/rust-lang/crates.io-index" 1714 | checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" 1715 | 1716 | [[package]] 1717 | name = "opaque-debug" 1718 | version = "0.3.0" 1719 | source = "registry+https://github.com/rust-lang/crates.io-index" 1720 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" 1721 | 1722 | [[package]] 1723 | name = "openssl" 1724 | version = "0.10.60" 1725 | source = "registry+https://github.com/rust-lang/crates.io-index" 1726 | checksum = "79a4c6c3a2b158f7f8f2a2fc5a969fa3a068df6fc9dbb4a43845436e3af7c800" 1727 | dependencies = [ 1728 | "bitflags 2.4.0", 1729 | "cfg-if", 1730 | "foreign-types", 1731 | "libc", 1732 | "once_cell", 1733 | "openssl-macros", 1734 | "openssl-sys", 1735 | ] 1736 | 1737 | [[package]] 1738 | name = "openssl-macros" 1739 | version = "0.1.0" 1740 | source = "registry+https://github.com/rust-lang/crates.io-index" 1741 | checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" 1742 | dependencies = [ 1743 | "proc-macro2", 1744 | "quote", 1745 | "syn 1.0.104", 1746 | ] 1747 | 1748 | [[package]] 1749 | name = "openssl-probe" 1750 | version = "0.1.5" 1751 | source = "registry+https://github.com/rust-lang/crates.io-index" 1752 | checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" 1753 | 1754 | [[package]] 1755 | name = "openssl-sys" 1756 | version = "0.9.96" 1757 | source = "registry+https://github.com/rust-lang/crates.io-index" 1758 | checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f" 1759 | dependencies = [ 1760 | "cc", 1761 | "libc", 1762 | "pkg-config", 1763 | "vcpkg", 1764 | ] 1765 | 1766 | [[package]] 1767 | name = "parking" 1768 | version = "2.0.0" 1769 | source = "registry+https://github.com/rust-lang/crates.io-index" 1770 | checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" 1771 | 1772 | [[package]] 1773 | name = "parking_lot" 1774 | version = "0.12.0" 1775 | source = "registry+https://github.com/rust-lang/crates.io-index" 1776 | checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" 1777 | dependencies = [ 1778 | "lock_api", 1779 | "parking_lot_core", 1780 | ] 1781 | 1782 | [[package]] 1783 | name = "parking_lot_core" 1784 | version = "0.9.3" 1785 | source = "registry+https://github.com/rust-lang/crates.io-index" 1786 | checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" 1787 | dependencies = [ 1788 | "cfg-if", 1789 | "libc", 1790 | "redox_syscall", 1791 | "smallvec", 1792 | "windows-sys 0.36.1", 1793 | ] 1794 | 1795 | [[package]] 1796 | name = "paste" 1797 | version = "1.0.6" 1798 | source = "registry+https://github.com/rust-lang/crates.io-index" 1799 | checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5" 1800 | 1801 | [[package]] 1802 | name = "percent-encoding" 1803 | version = "2.3.1" 1804 | source = "registry+https://github.com/rust-lang/crates.io-index" 1805 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 1806 | 1807 | [[package]] 1808 | name = "pin-project" 1809 | version = "1.0.10" 1810 | source = "registry+https://github.com/rust-lang/crates.io-index" 1811 | checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" 1812 | dependencies = [ 1813 | "pin-project-internal", 1814 | ] 1815 | 1816 | [[package]] 1817 | name = "pin-project-internal" 1818 | version = "1.0.10" 1819 | source = "registry+https://github.com/rust-lang/crates.io-index" 1820 | checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" 1821 | dependencies = [ 1822 | "proc-macro2", 1823 | "quote", 1824 | "syn 1.0.104", 1825 | ] 1826 | 1827 | [[package]] 1828 | name = "pin-project-lite" 1829 | version = "0.2.12" 1830 | source = "registry+https://github.com/rust-lang/crates.io-index" 1831 | checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" 1832 | 1833 | [[package]] 1834 | name = "pin-utils" 1835 | version = "0.1.0" 1836 | source = "registry+https://github.com/rust-lang/crates.io-index" 1837 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1838 | 1839 | [[package]] 1840 | name = "pkg-config" 1841 | version = "0.3.24" 1842 | source = "registry+https://github.com/rust-lang/crates.io-index" 1843 | checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" 1844 | 1845 | [[package]] 1846 | name = "polling" 1847 | version = "2.2.0" 1848 | source = "registry+https://github.com/rust-lang/crates.io-index" 1849 | checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" 1850 | dependencies = [ 1851 | "cfg-if", 1852 | "libc", 1853 | "log", 1854 | "wepoll-ffi", 1855 | "winapi", 1856 | ] 1857 | 1858 | [[package]] 1859 | name = "polyval" 1860 | version = "0.4.5" 1861 | source = "registry+https://github.com/rust-lang/crates.io-index" 1862 | checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd" 1863 | dependencies = [ 1864 | "cpuid-bool", 1865 | "opaque-debug", 1866 | "universal-hash", 1867 | ] 1868 | 1869 | [[package]] 1870 | name = "ppv-lite86" 1871 | version = "0.2.16" 1872 | source = "registry+https://github.com/rust-lang/crates.io-index" 1873 | checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" 1874 | 1875 | [[package]] 1876 | name = "pretty_env_logger" 1877 | version = "0.5.0" 1878 | source = "registry+https://github.com/rust-lang/crates.io-index" 1879 | checksum = "865724d4dbe39d9f3dd3b52b88d859d66bcb2d6a0acfd5ea68a65fb66d4bdc1c" 1880 | dependencies = [ 1881 | "env_logger", 1882 | "log", 1883 | ] 1884 | 1885 | [[package]] 1886 | name = "proc-macro-hack" 1887 | version = "0.5.19" 1888 | source = "registry+https://github.com/rust-lang/crates.io-index" 1889 | checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" 1890 | 1891 | [[package]] 1892 | name = "proc-macro2" 1893 | version = "1.0.74" 1894 | source = "registry+https://github.com/rust-lang/crates.io-index" 1895 | checksum = "2de98502f212cfcea8d0bb305bd0f49d7ebdd75b64ba0a68f937d888f4e0d6db" 1896 | dependencies = [ 1897 | "unicode-ident", 1898 | ] 1899 | 1900 | [[package]] 1901 | name = "quote" 1902 | version = "1.0.35" 1903 | source = "registry+https://github.com/rust-lang/crates.io-index" 1904 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 1905 | dependencies = [ 1906 | "proc-macro2", 1907 | ] 1908 | 1909 | [[package]] 1910 | name = "rand" 1911 | version = "0.7.3" 1912 | source = "registry+https://github.com/rust-lang/crates.io-index" 1913 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 1914 | dependencies = [ 1915 | "getrandom 0.1.16", 1916 | "libc", 1917 | "rand_chacha 0.2.2", 1918 | "rand_core 0.5.1", 1919 | "rand_hc", 1920 | ] 1921 | 1922 | [[package]] 1923 | name = "rand" 1924 | version = "0.8.5" 1925 | source = "registry+https://github.com/rust-lang/crates.io-index" 1926 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1927 | dependencies = [ 1928 | "libc", 1929 | "rand_chacha 0.3.1", 1930 | "rand_core 0.6.3", 1931 | ] 1932 | 1933 | [[package]] 1934 | name = "rand_chacha" 1935 | version = "0.2.2" 1936 | source = "registry+https://github.com/rust-lang/crates.io-index" 1937 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 1938 | dependencies = [ 1939 | "ppv-lite86", 1940 | "rand_core 0.5.1", 1941 | ] 1942 | 1943 | [[package]] 1944 | name = "rand_chacha" 1945 | version = "0.3.1" 1946 | source = "registry+https://github.com/rust-lang/crates.io-index" 1947 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1948 | dependencies = [ 1949 | "ppv-lite86", 1950 | "rand_core 0.6.3", 1951 | ] 1952 | 1953 | [[package]] 1954 | name = "rand_core" 1955 | version = "0.5.1" 1956 | source = "registry+https://github.com/rust-lang/crates.io-index" 1957 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 1958 | dependencies = [ 1959 | "getrandom 0.1.16", 1960 | ] 1961 | 1962 | [[package]] 1963 | name = "rand_core" 1964 | version = "0.6.3" 1965 | source = "registry+https://github.com/rust-lang/crates.io-index" 1966 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" 1967 | dependencies = [ 1968 | "getrandom 0.2.10", 1969 | ] 1970 | 1971 | [[package]] 1972 | name = "rand_hc" 1973 | version = "0.2.0" 1974 | source = "registry+https://github.com/rust-lang/crates.io-index" 1975 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 1976 | dependencies = [ 1977 | "rand_core 0.5.1", 1978 | ] 1979 | 1980 | [[package]] 1981 | name = "redox_syscall" 1982 | version = "0.2.11" 1983 | source = "registry+https://github.com/rust-lang/crates.io-index" 1984 | checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c" 1985 | dependencies = [ 1986 | "bitflags 1.3.2", 1987 | ] 1988 | 1989 | [[package]] 1990 | name = "regex" 1991 | version = "1.5.5" 1992 | source = "registry+https://github.com/rust-lang/crates.io-index" 1993 | checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" 1994 | dependencies = [ 1995 | "aho-corasick", 1996 | "memchr", 1997 | "regex-syntax", 1998 | ] 1999 | 2000 | [[package]] 2001 | name = "regex-syntax" 2002 | version = "0.6.25" 2003 | source = "registry+https://github.com/rust-lang/crates.io-index" 2004 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 2005 | 2006 | [[package]] 2007 | name = "remove_dir_all" 2008 | version = "0.5.3" 2009 | source = "registry+https://github.com/rust-lang/crates.io-index" 2010 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 2011 | dependencies = [ 2012 | "winapi", 2013 | ] 2014 | 2015 | [[package]] 2016 | name = "reqwest" 2017 | version = "0.11.26" 2018 | source = "registry+https://github.com/rust-lang/crates.io-index" 2019 | checksum = "78bf93c4af7a8bb7d879d51cebe797356ff10ae8516ace542b5182d9dcac10b2" 2020 | dependencies = [ 2021 | "base64 0.21.0", 2022 | "bytes", 2023 | "encoding_rs", 2024 | "futures-core", 2025 | "futures-util", 2026 | "h2", 2027 | "http 0.2.9", 2028 | "http-body", 2029 | "hyper", 2030 | "hyper-rustls", 2031 | "hyper-tls", 2032 | "ipnet", 2033 | "js-sys", 2034 | "log", 2035 | "mime", 2036 | "native-tls", 2037 | "once_cell", 2038 | "percent-encoding", 2039 | "pin-project-lite", 2040 | "rustls 0.21.6", 2041 | "rustls-native-certs", 2042 | "rustls-pemfile 1.0.0", 2043 | "serde", 2044 | "serde_json", 2045 | "serde_urlencoded", 2046 | "sync_wrapper", 2047 | "system-configuration", 2048 | "tokio", 2049 | "tokio-native-tls", 2050 | "tokio-rustls 0.24.0", 2051 | "tower-service", 2052 | "url", 2053 | "wasm-bindgen", 2054 | "wasm-bindgen-futures", 2055 | "web-sys", 2056 | "webpki-roots 0.25.2", 2057 | "winreg", 2058 | ] 2059 | 2060 | [[package]] 2061 | name = "ring" 2062 | version = "0.16.20" 2063 | source = "registry+https://github.com/rust-lang/crates.io-index" 2064 | checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" 2065 | dependencies = [ 2066 | "cc", 2067 | "libc", 2068 | "once_cell", 2069 | "spin", 2070 | "untrusted", 2071 | "web-sys", 2072 | "winapi", 2073 | ] 2074 | 2075 | [[package]] 2076 | name = "rustc-demangle" 2077 | version = "0.1.23" 2078 | source = "registry+https://github.com/rust-lang/crates.io-index" 2079 | checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" 2080 | 2081 | [[package]] 2082 | name = "rustc_version" 2083 | version = "0.2.3" 2084 | source = "registry+https://github.com/rust-lang/crates.io-index" 2085 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 2086 | dependencies = [ 2087 | "semver 0.9.0", 2088 | ] 2089 | 2090 | [[package]] 2091 | name = "rustc_version" 2092 | version = "0.4.0" 2093 | source = "registry+https://github.com/rust-lang/crates.io-index" 2094 | checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" 2095 | dependencies = [ 2096 | "semver 1.0.6", 2097 | ] 2098 | 2099 | [[package]] 2100 | name = "rustix" 2101 | version = "0.37.25" 2102 | source = "registry+https://github.com/rust-lang/crates.io-index" 2103 | checksum = "d4eb579851244c2c03e7c24f501c3432bed80b8f720af1d6e5b0e0f01555a035" 2104 | dependencies = [ 2105 | "bitflags 1.3.2", 2106 | "errno", 2107 | "io-lifetimes", 2108 | "libc", 2109 | "linux-raw-sys", 2110 | "windows-sys 0.48.0", 2111 | ] 2112 | 2113 | [[package]] 2114 | name = "rustls" 2115 | version = "0.20.4" 2116 | source = "registry+https://github.com/rust-lang/crates.io-index" 2117 | checksum = "4fbfeb8d0ddb84706bc597a5574ab8912817c52a397f819e5b614e2265206921" 2118 | dependencies = [ 2119 | "log", 2120 | "ring", 2121 | "sct", 2122 | "webpki", 2123 | ] 2124 | 2125 | [[package]] 2126 | name = "rustls" 2127 | version = "0.21.6" 2128 | source = "registry+https://github.com/rust-lang/crates.io-index" 2129 | checksum = "1d1feddffcfcc0b33f5c6ce9a29e341e4cd59c3f78e7ee45f4a40c038b1d6cbb" 2130 | dependencies = [ 2131 | "log", 2132 | "ring", 2133 | "rustls-webpki", 2134 | "sct", 2135 | ] 2136 | 2137 | [[package]] 2138 | name = "rustls-native-certs" 2139 | version = "0.6.1" 2140 | source = "registry+https://github.com/rust-lang/crates.io-index" 2141 | checksum = "5ca9ebdfa27d3fc180e42879037b5338ab1c040c06affd00d8338598e7800943" 2142 | dependencies = [ 2143 | "openssl-probe", 2144 | "rustls-pemfile 0.2.1", 2145 | "schannel", 2146 | "security-framework", 2147 | ] 2148 | 2149 | [[package]] 2150 | name = "rustls-pemfile" 2151 | version = "0.2.1" 2152 | source = "registry+https://github.com/rust-lang/crates.io-index" 2153 | checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" 2154 | dependencies = [ 2155 | "base64 0.13.0", 2156 | ] 2157 | 2158 | [[package]] 2159 | name = "rustls-pemfile" 2160 | version = "1.0.0" 2161 | source = "registry+https://github.com/rust-lang/crates.io-index" 2162 | checksum = "e7522c9de787ff061458fe9a829dc790a3f5b22dc571694fc5883f448b94d9a9" 2163 | dependencies = [ 2164 | "base64 0.13.0", 2165 | ] 2166 | 2167 | [[package]] 2168 | name = "rustls-webpki" 2169 | version = "0.101.4" 2170 | source = "registry+https://github.com/rust-lang/crates.io-index" 2171 | checksum = "7d93931baf2d282fff8d3a532bbfd7653f734643161b87e3e01e59a04439bf0d" 2172 | dependencies = [ 2173 | "ring", 2174 | "untrusted", 2175 | ] 2176 | 2177 | [[package]] 2178 | name = "ryu" 2179 | version = "1.0.9" 2180 | source = "registry+https://github.com/rust-lang/crates.io-index" 2181 | checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" 2182 | 2183 | [[package]] 2184 | name = "schannel" 2185 | version = "0.1.19" 2186 | source = "registry+https://github.com/rust-lang/crates.io-index" 2187 | checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" 2188 | dependencies = [ 2189 | "lazy_static", 2190 | "winapi", 2191 | ] 2192 | 2193 | [[package]] 2194 | name = "scopeguard" 2195 | version = "1.1.0" 2196 | source = "registry+https://github.com/rust-lang/crates.io-index" 2197 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 2198 | 2199 | [[package]] 2200 | name = "sct" 2201 | version = "0.7.0" 2202 | source = "registry+https://github.com/rust-lang/crates.io-index" 2203 | checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" 2204 | dependencies = [ 2205 | "ring", 2206 | "untrusted", 2207 | ] 2208 | 2209 | [[package]] 2210 | name = "security-framework" 2211 | version = "2.6.1" 2212 | source = "registry+https://github.com/rust-lang/crates.io-index" 2213 | checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" 2214 | dependencies = [ 2215 | "bitflags 1.3.2", 2216 | "core-foundation", 2217 | "core-foundation-sys", 2218 | "libc", 2219 | "security-framework-sys", 2220 | ] 2221 | 2222 | [[package]] 2223 | name = "security-framework-sys" 2224 | version = "2.6.1" 2225 | source = "registry+https://github.com/rust-lang/crates.io-index" 2226 | checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" 2227 | dependencies = [ 2228 | "core-foundation-sys", 2229 | "libc", 2230 | ] 2231 | 2232 | [[package]] 2233 | name = "semver" 2234 | version = "0.9.0" 2235 | source = "registry+https://github.com/rust-lang/crates.io-index" 2236 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 2237 | dependencies = [ 2238 | "semver-parser", 2239 | ] 2240 | 2241 | [[package]] 2242 | name = "semver" 2243 | version = "1.0.6" 2244 | source = "registry+https://github.com/rust-lang/crates.io-index" 2245 | checksum = "a4a3381e03edd24287172047536f20cabde766e2cd3e65e6b00fb3af51c4f38d" 2246 | 2247 | [[package]] 2248 | name = "semver-parser" 2249 | version = "0.7.0" 2250 | source = "registry+https://github.com/rust-lang/crates.io-index" 2251 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 2252 | 2253 | [[package]] 2254 | name = "serde" 2255 | version = "1.0.197" 2256 | source = "registry+https://github.com/rust-lang/crates.io-index" 2257 | checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" 2258 | dependencies = [ 2259 | "serde_derive", 2260 | ] 2261 | 2262 | [[package]] 2263 | name = "serde_derive" 2264 | version = "1.0.197" 2265 | source = "registry+https://github.com/rust-lang/crates.io-index" 2266 | checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" 2267 | dependencies = [ 2268 | "proc-macro2", 2269 | "quote", 2270 | "syn 2.0.46", 2271 | ] 2272 | 2273 | [[package]] 2274 | name = "serde_json" 2275 | version = "1.0.114" 2276 | source = "registry+https://github.com/rust-lang/crates.io-index" 2277 | checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" 2278 | dependencies = [ 2279 | "itoa", 2280 | "ryu", 2281 | "serde", 2282 | ] 2283 | 2284 | [[package]] 2285 | name = "serde_qs" 2286 | version = "0.8.5" 2287 | source = "registry+https://github.com/rust-lang/crates.io-index" 2288 | checksum = "c7715380eec75f029a4ef7de39a9200e0a63823176b759d055b613f5a87df6a6" 2289 | dependencies = [ 2290 | "percent-encoding", 2291 | "serde", 2292 | "thiserror", 2293 | ] 2294 | 2295 | [[package]] 2296 | name = "serde_urlencoded" 2297 | version = "0.7.1" 2298 | source = "registry+https://github.com/rust-lang/crates.io-index" 2299 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 2300 | dependencies = [ 2301 | "form_urlencoded", 2302 | "itoa", 2303 | "ryu", 2304 | "serde", 2305 | ] 2306 | 2307 | [[package]] 2308 | name = "sha1" 2309 | version = "0.6.1" 2310 | source = "registry+https://github.com/rust-lang/crates.io-index" 2311 | checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" 2312 | dependencies = [ 2313 | "sha1_smol", 2314 | ] 2315 | 2316 | [[package]] 2317 | name = "sha1" 2318 | version = "0.10.5" 2319 | source = "registry+https://github.com/rust-lang/crates.io-index" 2320 | checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" 2321 | dependencies = [ 2322 | "cfg-if", 2323 | "cpufeatures", 2324 | "digest 0.10.6", 2325 | ] 2326 | 2327 | [[package]] 2328 | name = "sha1_smol" 2329 | version = "1.0.0" 2330 | source = "registry+https://github.com/rust-lang/crates.io-index" 2331 | checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" 2332 | 2333 | [[package]] 2334 | name = "sha2" 2335 | version = "0.9.9" 2336 | source = "registry+https://github.com/rust-lang/crates.io-index" 2337 | checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" 2338 | dependencies = [ 2339 | "block-buffer 0.9.0", 2340 | "cfg-if", 2341 | "cpufeatures", 2342 | "digest 0.9.0", 2343 | "opaque-debug", 2344 | ] 2345 | 2346 | [[package]] 2347 | name = "signal-hook-registry" 2348 | version = "1.4.0" 2349 | source = "registry+https://github.com/rust-lang/crates.io-index" 2350 | checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" 2351 | dependencies = [ 2352 | "libc", 2353 | ] 2354 | 2355 | [[package]] 2356 | name = "simple-mutex" 2357 | version = "1.1.5" 2358 | source = "registry+https://github.com/rust-lang/crates.io-index" 2359 | checksum = "38aabbeafa6f6dead8cebf246fe9fae1f9215c8d29b3a69f93bd62a9e4a3dcd6" 2360 | dependencies = [ 2361 | "event-listener", 2362 | ] 2363 | 2364 | [[package]] 2365 | name = "slab" 2366 | version = "0.4.5" 2367 | source = "registry+https://github.com/rust-lang/crates.io-index" 2368 | checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" 2369 | 2370 | [[package]] 2371 | name = "smallvec" 2372 | version = "1.8.0" 2373 | source = "registry+https://github.com/rust-lang/crates.io-index" 2374 | checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" 2375 | 2376 | [[package]] 2377 | name = "socket2" 2378 | version = "0.4.9" 2379 | source = "registry+https://github.com/rust-lang/crates.io-index" 2380 | checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" 2381 | dependencies = [ 2382 | "libc", 2383 | "winapi", 2384 | ] 2385 | 2386 | [[package]] 2387 | name = "socket2" 2388 | version = "0.5.5" 2389 | source = "registry+https://github.com/rust-lang/crates.io-index" 2390 | checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" 2391 | dependencies = [ 2392 | "libc", 2393 | "windows-sys 0.48.0", 2394 | ] 2395 | 2396 | [[package]] 2397 | name = "spin" 2398 | version = "0.5.2" 2399 | source = "registry+https://github.com/rust-lang/crates.io-index" 2400 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 2401 | 2402 | [[package]] 2403 | name = "standback" 2404 | version = "0.2.17" 2405 | source = "registry+https://github.com/rust-lang/crates.io-index" 2406 | checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" 2407 | dependencies = [ 2408 | "version_check", 2409 | ] 2410 | 2411 | [[package]] 2412 | name = "static_assertions" 2413 | version = "1.1.0" 2414 | source = "registry+https://github.com/rust-lang/crates.io-index" 2415 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 2416 | 2417 | [[package]] 2418 | name = "stdweb" 2419 | version = "0.4.20" 2420 | source = "registry+https://github.com/rust-lang/crates.io-index" 2421 | checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" 2422 | dependencies = [ 2423 | "discard", 2424 | "rustc_version 0.2.3", 2425 | "stdweb-derive", 2426 | "stdweb-internal-macros", 2427 | "stdweb-internal-runtime", 2428 | "wasm-bindgen", 2429 | ] 2430 | 2431 | [[package]] 2432 | name = "stdweb-derive" 2433 | version = "0.5.3" 2434 | source = "registry+https://github.com/rust-lang/crates.io-index" 2435 | checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" 2436 | dependencies = [ 2437 | "proc-macro2", 2438 | "quote", 2439 | "serde", 2440 | "serde_derive", 2441 | "syn 1.0.104", 2442 | ] 2443 | 2444 | [[package]] 2445 | name = "stdweb-internal-macros" 2446 | version = "0.2.9" 2447 | source = "registry+https://github.com/rust-lang/crates.io-index" 2448 | checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" 2449 | dependencies = [ 2450 | "base-x", 2451 | "proc-macro2", 2452 | "quote", 2453 | "serde", 2454 | "serde_derive", 2455 | "serde_json", 2456 | "sha1 0.6.1", 2457 | "syn 1.0.104", 2458 | ] 2459 | 2460 | [[package]] 2461 | name = "stdweb-internal-runtime" 2462 | version = "0.1.5" 2463 | source = "registry+https://github.com/rust-lang/crates.io-index" 2464 | checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" 2465 | 2466 | [[package]] 2467 | name = "stream-reconnect" 2468 | version = "0.4.0-beta.4" 2469 | source = "registry+https://github.com/rust-lang/crates.io-index" 2470 | checksum = "7831822002373adc1e139db41370c7723baa81a7922cecb20dde12970b9e8bc2" 2471 | dependencies = [ 2472 | "async-std", 2473 | "futures", 2474 | "log", 2475 | "rand 0.8.5", 2476 | "tokio", 2477 | ] 2478 | 2479 | [[package]] 2480 | name = "subtle" 2481 | version = "2.4.1" 2482 | source = "registry+https://github.com/rust-lang/crates.io-index" 2483 | checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" 2484 | 2485 | [[package]] 2486 | name = "syn" 2487 | version = "1.0.104" 2488 | source = "registry+https://github.com/rust-lang/crates.io-index" 2489 | checksum = "4ae548ec36cf198c0ef7710d3c230987c2d6d7bd98ad6edc0274462724c585ce" 2490 | dependencies = [ 2491 | "proc-macro2", 2492 | "quote", 2493 | "unicode-ident", 2494 | ] 2495 | 2496 | [[package]] 2497 | name = "syn" 2498 | version = "2.0.46" 2499 | source = "registry+https://github.com/rust-lang/crates.io-index" 2500 | checksum = "89456b690ff72fddcecf231caedbe615c59480c93358a93dfae7fc29e3ebbf0e" 2501 | dependencies = [ 2502 | "proc-macro2", 2503 | "quote", 2504 | "unicode-ident", 2505 | ] 2506 | 2507 | [[package]] 2508 | name = "sync_wrapper" 2509 | version = "0.1.2" 2510 | source = "registry+https://github.com/rust-lang/crates.io-index" 2511 | checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" 2512 | 2513 | [[package]] 2514 | name = "system-configuration" 2515 | version = "0.5.1" 2516 | source = "registry+https://github.com/rust-lang/crates.io-index" 2517 | checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" 2518 | dependencies = [ 2519 | "bitflags 1.3.2", 2520 | "core-foundation", 2521 | "system-configuration-sys", 2522 | ] 2523 | 2524 | [[package]] 2525 | name = "system-configuration-sys" 2526 | version = "0.5.0" 2527 | source = "registry+https://github.com/rust-lang/crates.io-index" 2528 | checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" 2529 | dependencies = [ 2530 | "core-foundation-sys", 2531 | "libc", 2532 | ] 2533 | 2534 | [[package]] 2535 | name = "tempfile" 2536 | version = "3.3.0" 2537 | source = "registry+https://github.com/rust-lang/crates.io-index" 2538 | checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" 2539 | dependencies = [ 2540 | "cfg-if", 2541 | "fastrand", 2542 | "libc", 2543 | "redox_syscall", 2544 | "remove_dir_all", 2545 | "winapi", 2546 | ] 2547 | 2548 | [[package]] 2549 | name = "termcolor" 2550 | version = "1.1.3" 2551 | source = "registry+https://github.com/rust-lang/crates.io-index" 2552 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 2553 | dependencies = [ 2554 | "winapi-util", 2555 | ] 2556 | 2557 | [[package]] 2558 | name = "thiserror" 2559 | version = "1.0.58" 2560 | source = "registry+https://github.com/rust-lang/crates.io-index" 2561 | checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" 2562 | dependencies = [ 2563 | "thiserror-impl", 2564 | ] 2565 | 2566 | [[package]] 2567 | name = "thiserror-impl" 2568 | version = "1.0.58" 2569 | source = "registry+https://github.com/rust-lang/crates.io-index" 2570 | checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" 2571 | dependencies = [ 2572 | "proc-macro2", 2573 | "quote", 2574 | "syn 2.0.46", 2575 | ] 2576 | 2577 | [[package]] 2578 | name = "time" 2579 | version = "0.2.27" 2580 | source = "registry+https://github.com/rust-lang/crates.io-index" 2581 | checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" 2582 | dependencies = [ 2583 | "const_fn", 2584 | "libc", 2585 | "standback", 2586 | "stdweb", 2587 | "time-macros 0.1.1", 2588 | "version_check", 2589 | "winapi", 2590 | ] 2591 | 2592 | [[package]] 2593 | name = "time" 2594 | version = "0.3.7" 2595 | source = "registry+https://github.com/rust-lang/crates.io-index" 2596 | checksum = "004cbc98f30fa233c61a38bc77e96a9106e65c88f2d3bef182ae952027e5753d" 2597 | dependencies = [ 2598 | "itoa", 2599 | "libc", 2600 | "num_threads", 2601 | "time-macros 0.2.3", 2602 | ] 2603 | 2604 | [[package]] 2605 | name = "time-macros" 2606 | version = "0.1.1" 2607 | source = "registry+https://github.com/rust-lang/crates.io-index" 2608 | checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" 2609 | dependencies = [ 2610 | "proc-macro-hack", 2611 | "time-macros-impl", 2612 | ] 2613 | 2614 | [[package]] 2615 | name = "time-macros" 2616 | version = "0.2.3" 2617 | source = "registry+https://github.com/rust-lang/crates.io-index" 2618 | checksum = "25eb0ca3468fc0acc11828786797f6ef9aa1555e4a211a60d64cc8e4d1be47d6" 2619 | 2620 | [[package]] 2621 | name = "time-macros-impl" 2622 | version = "0.1.2" 2623 | source = "registry+https://github.com/rust-lang/crates.io-index" 2624 | checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" 2625 | dependencies = [ 2626 | "proc-macro-hack", 2627 | "proc-macro2", 2628 | "quote", 2629 | "standback", 2630 | "syn 1.0.104", 2631 | ] 2632 | 2633 | [[package]] 2634 | name = "tinyvec" 2635 | version = "1.5.1" 2636 | source = "registry+https://github.com/rust-lang/crates.io-index" 2637 | checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" 2638 | dependencies = [ 2639 | "tinyvec_macros", 2640 | ] 2641 | 2642 | [[package]] 2643 | name = "tinyvec_macros" 2644 | version = "0.1.0" 2645 | source = "registry+https://github.com/rust-lang/crates.io-index" 2646 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 2647 | 2648 | [[package]] 2649 | name = "tokio" 2650 | version = "1.36.0" 2651 | source = "registry+https://github.com/rust-lang/crates.io-index" 2652 | checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" 2653 | dependencies = [ 2654 | "backtrace", 2655 | "bytes", 2656 | "libc", 2657 | "mio", 2658 | "num_cpus", 2659 | "parking_lot", 2660 | "pin-project-lite", 2661 | "signal-hook-registry", 2662 | "socket2 0.5.5", 2663 | "tokio-macros", 2664 | "windows-sys 0.48.0", 2665 | ] 2666 | 2667 | [[package]] 2668 | name = "tokio-macros" 2669 | version = "2.2.0" 2670 | source = "registry+https://github.com/rust-lang/crates.io-index" 2671 | checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" 2672 | dependencies = [ 2673 | "proc-macro2", 2674 | "quote", 2675 | "syn 2.0.46", 2676 | ] 2677 | 2678 | [[package]] 2679 | name = "tokio-native-tls" 2680 | version = "0.3.0" 2681 | source = "registry+https://github.com/rust-lang/crates.io-index" 2682 | checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" 2683 | dependencies = [ 2684 | "native-tls", 2685 | "tokio", 2686 | ] 2687 | 2688 | [[package]] 2689 | name = "tokio-openssl" 2690 | version = "0.6.3" 2691 | source = "registry+https://github.com/rust-lang/crates.io-index" 2692 | checksum = "c08f9ffb7809f1b20c1b398d92acf4cc719874b3b2b2d9ea2f09b4a80350878a" 2693 | dependencies = [ 2694 | "futures-util", 2695 | "openssl", 2696 | "openssl-sys", 2697 | "tokio", 2698 | ] 2699 | 2700 | [[package]] 2701 | name = "tokio-rustls" 2702 | version = "0.23.2" 2703 | source = "registry+https://github.com/rust-lang/crates.io-index" 2704 | checksum = "a27d5f2b839802bd8267fa19b0530f5a08b9c08cd417976be2a65d130fe1c11b" 2705 | dependencies = [ 2706 | "rustls 0.20.4", 2707 | "tokio", 2708 | "webpki", 2709 | ] 2710 | 2711 | [[package]] 2712 | name = "tokio-rustls" 2713 | version = "0.24.0" 2714 | source = "registry+https://github.com/rust-lang/crates.io-index" 2715 | checksum = "e0d409377ff5b1e3ca6437aa86c1eb7d40c134bfec254e44c830defa92669db5" 2716 | dependencies = [ 2717 | "rustls 0.21.6", 2718 | "tokio", 2719 | ] 2720 | 2721 | [[package]] 2722 | name = "tokio-stream" 2723 | version = "0.1.8" 2724 | source = "registry+https://github.com/rust-lang/crates.io-index" 2725 | checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" 2726 | dependencies = [ 2727 | "futures-core", 2728 | "pin-project-lite", 2729 | "tokio", 2730 | ] 2731 | 2732 | [[package]] 2733 | name = "tokio-test" 2734 | version = "0.4.3" 2735 | source = "registry+https://github.com/rust-lang/crates.io-index" 2736 | checksum = "e89b3cbabd3ae862100094ae433e1def582cf86451b4e9bf83aa7ac1d8a7d719" 2737 | dependencies = [ 2738 | "async-stream", 2739 | "bytes", 2740 | "futures-core", 2741 | "tokio", 2742 | "tokio-stream", 2743 | ] 2744 | 2745 | [[package]] 2746 | name = "tokio-util" 2747 | version = "0.7.8" 2748 | source = "registry+https://github.com/rust-lang/crates.io-index" 2749 | checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" 2750 | dependencies = [ 2751 | "bytes", 2752 | "futures-core", 2753 | "futures-sink", 2754 | "pin-project-lite", 2755 | "tokio", 2756 | "tracing", 2757 | ] 2758 | 2759 | [[package]] 2760 | name = "tower-service" 2761 | version = "0.3.1" 2762 | source = "registry+https://github.com/rust-lang/crates.io-index" 2763 | checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" 2764 | 2765 | [[package]] 2766 | name = "tracing" 2767 | version = "0.1.40" 2768 | source = "registry+https://github.com/rust-lang/crates.io-index" 2769 | checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" 2770 | dependencies = [ 2771 | "log", 2772 | "pin-project-lite", 2773 | "tracing-core", 2774 | ] 2775 | 2776 | [[package]] 2777 | name = "tracing-core" 2778 | version = "0.1.32" 2779 | source = "registry+https://github.com/rust-lang/crates.io-index" 2780 | checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" 2781 | dependencies = [ 2782 | "once_cell", 2783 | ] 2784 | 2785 | [[package]] 2786 | name = "try-lock" 2787 | version = "0.2.3" 2788 | source = "registry+https://github.com/rust-lang/crates.io-index" 2789 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 2790 | 2791 | [[package]] 2792 | name = "tungstenite" 2793 | version = "0.20.1" 2794 | source = "registry+https://github.com/rust-lang/crates.io-index" 2795 | checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" 2796 | dependencies = [ 2797 | "byteorder", 2798 | "bytes", 2799 | "data-encoding", 2800 | "http 0.2.9", 2801 | "httparse", 2802 | "log", 2803 | "native-tls", 2804 | "rand 0.8.5", 2805 | "rustls 0.21.6", 2806 | "sha1 0.10.5", 2807 | "thiserror", 2808 | "url", 2809 | "utf-8", 2810 | ] 2811 | 2812 | [[package]] 2813 | name = "typenum" 2814 | version = "1.15.0" 2815 | source = "registry+https://github.com/rust-lang/crates.io-index" 2816 | checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" 2817 | 2818 | [[package]] 2819 | name = "unicode-bidi" 2820 | version = "0.3.13" 2821 | source = "registry+https://github.com/rust-lang/crates.io-index" 2822 | checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" 2823 | 2824 | [[package]] 2825 | name = "unicode-ident" 2826 | version = "1.0.5" 2827 | source = "registry+https://github.com/rust-lang/crates.io-index" 2828 | checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" 2829 | 2830 | [[package]] 2831 | name = "unicode-normalization" 2832 | version = "0.1.22" 2833 | source = "registry+https://github.com/rust-lang/crates.io-index" 2834 | checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" 2835 | dependencies = [ 2836 | "tinyvec", 2837 | ] 2838 | 2839 | [[package]] 2840 | name = "universal-hash" 2841 | version = "0.4.1" 2842 | source = "registry+https://github.com/rust-lang/crates.io-index" 2843 | checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" 2844 | dependencies = [ 2845 | "generic-array", 2846 | "subtle", 2847 | ] 2848 | 2849 | [[package]] 2850 | name = "untrusted" 2851 | version = "0.7.1" 2852 | source = "registry+https://github.com/rust-lang/crates.io-index" 2853 | checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" 2854 | 2855 | [[package]] 2856 | name = "url" 2857 | version = "2.5.0" 2858 | source = "registry+https://github.com/rust-lang/crates.io-index" 2859 | checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" 2860 | dependencies = [ 2861 | "form_urlencoded", 2862 | "idna", 2863 | "percent-encoding", 2864 | "serde", 2865 | ] 2866 | 2867 | [[package]] 2868 | name = "utf-8" 2869 | version = "0.7.6" 2870 | source = "registry+https://github.com/rust-lang/crates.io-index" 2871 | checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" 2872 | 2873 | [[package]] 2874 | name = "value-bag" 2875 | version = "1.7.0" 2876 | source = "registry+https://github.com/rust-lang/crates.io-index" 2877 | checksum = "126e423afe2dd9ac52142e7e9d5ce4135d7e13776c529d27fd6bc49f19e3280b" 2878 | 2879 | [[package]] 2880 | name = "vcpkg" 2881 | version = "0.2.15" 2882 | source = "registry+https://github.com/rust-lang/crates.io-index" 2883 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 2884 | 2885 | [[package]] 2886 | name = "version_check" 2887 | version = "0.9.4" 2888 | source = "registry+https://github.com/rust-lang/crates.io-index" 2889 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 2890 | 2891 | [[package]] 2892 | name = "waker-fn" 2893 | version = "1.1.0" 2894 | source = "registry+https://github.com/rust-lang/crates.io-index" 2895 | checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" 2896 | 2897 | [[package]] 2898 | name = "want" 2899 | version = "0.3.0" 2900 | source = "registry+https://github.com/rust-lang/crates.io-index" 2901 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 2902 | dependencies = [ 2903 | "log", 2904 | "try-lock", 2905 | ] 2906 | 2907 | [[package]] 2908 | name = "wasi" 2909 | version = "0.9.0+wasi-snapshot-preview1" 2910 | source = "registry+https://github.com/rust-lang/crates.io-index" 2911 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 2912 | 2913 | [[package]] 2914 | name = "wasi" 2915 | version = "0.11.0+wasi-snapshot-preview1" 2916 | source = "registry+https://github.com/rust-lang/crates.io-index" 2917 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 2918 | 2919 | [[package]] 2920 | name = "wasm-bindgen" 2921 | version = "0.2.79" 2922 | source = "registry+https://github.com/rust-lang/crates.io-index" 2923 | checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" 2924 | dependencies = [ 2925 | "cfg-if", 2926 | "wasm-bindgen-macro", 2927 | ] 2928 | 2929 | [[package]] 2930 | name = "wasm-bindgen-backend" 2931 | version = "0.2.79" 2932 | source = "registry+https://github.com/rust-lang/crates.io-index" 2933 | checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" 2934 | dependencies = [ 2935 | "bumpalo", 2936 | "lazy_static", 2937 | "log", 2938 | "proc-macro2", 2939 | "quote", 2940 | "syn 1.0.104", 2941 | "wasm-bindgen-shared", 2942 | ] 2943 | 2944 | [[package]] 2945 | name = "wasm-bindgen-futures" 2946 | version = "0.4.29" 2947 | source = "registry+https://github.com/rust-lang/crates.io-index" 2948 | checksum = "2eb6ec270a31b1d3c7e266b999739109abce8b6c87e4b31fcfcd788b65267395" 2949 | dependencies = [ 2950 | "cfg-if", 2951 | "js-sys", 2952 | "wasm-bindgen", 2953 | "web-sys", 2954 | ] 2955 | 2956 | [[package]] 2957 | name = "wasm-bindgen-macro" 2958 | version = "0.2.79" 2959 | source = "registry+https://github.com/rust-lang/crates.io-index" 2960 | checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" 2961 | dependencies = [ 2962 | "quote", 2963 | "wasm-bindgen-macro-support", 2964 | ] 2965 | 2966 | [[package]] 2967 | name = "wasm-bindgen-macro-support" 2968 | version = "0.2.79" 2969 | source = "registry+https://github.com/rust-lang/crates.io-index" 2970 | checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" 2971 | dependencies = [ 2972 | "proc-macro2", 2973 | "quote", 2974 | "syn 1.0.104", 2975 | "wasm-bindgen-backend", 2976 | "wasm-bindgen-shared", 2977 | ] 2978 | 2979 | [[package]] 2980 | name = "wasm-bindgen-shared" 2981 | version = "0.2.79" 2982 | source = "registry+https://github.com/rust-lang/crates.io-index" 2983 | checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" 2984 | 2985 | [[package]] 2986 | name = "web-sys" 2987 | version = "0.3.56" 2988 | source = "registry+https://github.com/rust-lang/crates.io-index" 2989 | checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" 2990 | dependencies = [ 2991 | "js-sys", 2992 | "wasm-bindgen", 2993 | ] 2994 | 2995 | [[package]] 2996 | name = "webpki" 2997 | version = "0.22.2" 2998 | source = "registry+https://github.com/rust-lang/crates.io-index" 2999 | checksum = "07ecc0cd7cac091bf682ec5efa18b1cff79d617b84181f38b3951dbe135f607f" 3000 | dependencies = [ 3001 | "ring", 3002 | "untrusted", 3003 | ] 3004 | 3005 | [[package]] 3006 | name = "webpki-roots" 3007 | version = "0.22.2" 3008 | source = "registry+https://github.com/rust-lang/crates.io-index" 3009 | checksum = "552ceb903e957524388c4d3475725ff2c8b7960922063af6ce53c9a43da07449" 3010 | dependencies = [ 3011 | "webpki", 3012 | ] 3013 | 3014 | [[package]] 3015 | name = "webpki-roots" 3016 | version = "0.25.2" 3017 | source = "registry+https://github.com/rust-lang/crates.io-index" 3018 | checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" 3019 | 3020 | [[package]] 3021 | name = "wepoll-ffi" 3022 | version = "0.1.2" 3023 | source = "registry+https://github.com/rust-lang/crates.io-index" 3024 | checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" 3025 | dependencies = [ 3026 | "cc", 3027 | ] 3028 | 3029 | [[package]] 3030 | name = "winapi" 3031 | version = "0.3.9" 3032 | source = "registry+https://github.com/rust-lang/crates.io-index" 3033 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 3034 | dependencies = [ 3035 | "winapi-i686-pc-windows-gnu", 3036 | "winapi-x86_64-pc-windows-gnu", 3037 | ] 3038 | 3039 | [[package]] 3040 | name = "winapi-i686-pc-windows-gnu" 3041 | version = "0.4.0" 3042 | source = "registry+https://github.com/rust-lang/crates.io-index" 3043 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 3044 | 3045 | [[package]] 3046 | name = "winapi-util" 3047 | version = "0.1.5" 3048 | source = "registry+https://github.com/rust-lang/crates.io-index" 3049 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 3050 | dependencies = [ 3051 | "winapi", 3052 | ] 3053 | 3054 | [[package]] 3055 | name = "winapi-x86_64-pc-windows-gnu" 3056 | version = "0.4.0" 3057 | source = "registry+https://github.com/rust-lang/crates.io-index" 3058 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 3059 | 3060 | [[package]] 3061 | name = "windows-sys" 3062 | version = "0.36.1" 3063 | source = "registry+https://github.com/rust-lang/crates.io-index" 3064 | checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" 3065 | dependencies = [ 3066 | "windows_aarch64_msvc 0.36.1", 3067 | "windows_i686_gnu 0.36.1", 3068 | "windows_i686_msvc 0.36.1", 3069 | "windows_x86_64_gnu 0.36.1", 3070 | "windows_x86_64_msvc 0.36.1", 3071 | ] 3072 | 3073 | [[package]] 3074 | name = "windows-sys" 3075 | version = "0.48.0" 3076 | source = "registry+https://github.com/rust-lang/crates.io-index" 3077 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 3078 | dependencies = [ 3079 | "windows-targets", 3080 | ] 3081 | 3082 | [[package]] 3083 | name = "windows-targets" 3084 | version = "0.48.0" 3085 | source = "registry+https://github.com/rust-lang/crates.io-index" 3086 | checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" 3087 | dependencies = [ 3088 | "windows_aarch64_gnullvm", 3089 | "windows_aarch64_msvc 0.48.0", 3090 | "windows_i686_gnu 0.48.0", 3091 | "windows_i686_msvc 0.48.0", 3092 | "windows_x86_64_gnu 0.48.0", 3093 | "windows_x86_64_gnullvm", 3094 | "windows_x86_64_msvc 0.48.0", 3095 | ] 3096 | 3097 | [[package]] 3098 | name = "windows_aarch64_gnullvm" 3099 | version = "0.48.0" 3100 | source = "registry+https://github.com/rust-lang/crates.io-index" 3101 | checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" 3102 | 3103 | [[package]] 3104 | name = "windows_aarch64_msvc" 3105 | version = "0.36.1" 3106 | source = "registry+https://github.com/rust-lang/crates.io-index" 3107 | checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" 3108 | 3109 | [[package]] 3110 | name = "windows_aarch64_msvc" 3111 | version = "0.48.0" 3112 | source = "registry+https://github.com/rust-lang/crates.io-index" 3113 | checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" 3114 | 3115 | [[package]] 3116 | name = "windows_i686_gnu" 3117 | version = "0.36.1" 3118 | source = "registry+https://github.com/rust-lang/crates.io-index" 3119 | checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" 3120 | 3121 | [[package]] 3122 | name = "windows_i686_gnu" 3123 | version = "0.48.0" 3124 | source = "registry+https://github.com/rust-lang/crates.io-index" 3125 | checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" 3126 | 3127 | [[package]] 3128 | name = "windows_i686_msvc" 3129 | version = "0.36.1" 3130 | source = "registry+https://github.com/rust-lang/crates.io-index" 3131 | checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" 3132 | 3133 | [[package]] 3134 | name = "windows_i686_msvc" 3135 | version = "0.48.0" 3136 | source = "registry+https://github.com/rust-lang/crates.io-index" 3137 | checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" 3138 | 3139 | [[package]] 3140 | name = "windows_x86_64_gnu" 3141 | version = "0.36.1" 3142 | source = "registry+https://github.com/rust-lang/crates.io-index" 3143 | checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" 3144 | 3145 | [[package]] 3146 | name = "windows_x86_64_gnu" 3147 | version = "0.48.0" 3148 | source = "registry+https://github.com/rust-lang/crates.io-index" 3149 | checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" 3150 | 3151 | [[package]] 3152 | name = "windows_x86_64_gnullvm" 3153 | version = "0.48.0" 3154 | source = "registry+https://github.com/rust-lang/crates.io-index" 3155 | checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" 3156 | 3157 | [[package]] 3158 | name = "windows_x86_64_msvc" 3159 | version = "0.36.1" 3160 | source = "registry+https://github.com/rust-lang/crates.io-index" 3161 | checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" 3162 | 3163 | [[package]] 3164 | name = "windows_x86_64_msvc" 3165 | version = "0.48.0" 3166 | source = "registry+https://github.com/rust-lang/crates.io-index" 3167 | checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" 3168 | 3169 | [[package]] 3170 | name = "winreg" 3171 | version = "0.50.0" 3172 | source = "registry+https://github.com/rust-lang/crates.io-index" 3173 | checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" 3174 | dependencies = [ 3175 | "cfg-if", 3176 | "windows-sys 0.48.0", 3177 | ] 3178 | 3179 | [[package]] 3180 | name = "zstd" 3181 | version = "0.13.0" 3182 | source = "registry+https://github.com/rust-lang/crates.io-index" 3183 | checksum = "bffb3309596d527cfcba7dfc6ed6052f1d39dfbd7c867aa2e865e4a449c10110" 3184 | dependencies = [ 3185 | "zstd-safe", 3186 | ] 3187 | 3188 | [[package]] 3189 | name = "zstd-safe" 3190 | version = "7.0.0" 3191 | source = "registry+https://github.com/rust-lang/crates.io-index" 3192 | checksum = "43747c7422e2924c11144d5229878b98180ef8b06cca4ab5af37afc8a8d8ea3e" 3193 | dependencies = [ 3194 | "zstd-sys", 3195 | ] 3196 | 3197 | [[package]] 3198 | name = "zstd-sys" 3199 | version = "2.0.8+zstd.1.5.5" 3200 | source = "registry+https://github.com/rust-lang/crates.io-index" 3201 | checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" 3202 | dependencies = [ 3203 | "cc", 3204 | "libc", 3205 | "pkg-config", 3206 | ] 3207 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["actix-bililive", "bililive", "bililive-core"] 3 | -------------------------------------------------------------------------------- /Justfile: -------------------------------------------------------------------------------- 1 | ci: fmt clippy 2 | 3 | fmt: 4 | cargo fmt -- --check 5 | 6 | clippy: 7 | cd ./bililive-core && cargo clippy 8 | cd ./bililive && cargo clippy 9 | cd ./actix-bililive && cargo clippy 10 | 11 | clippy-pedantic: 12 | cd ./bililive-core && cargo clippy -- -W clippy::all -W clippy::pedantic -W clippy::nursery 13 | cd ./bililive && cargo clippy -- -W clippy::all -W clippy::pedantic -W clippy::nursery 14 | cd ./actix-bililive && cargo clippy -- -W clippy::all -W clippy::pedantic -W clippy::nursery -A clippy::future_not_send 15 | 16 | test $FAST_TEST="1": 17 | cd ./bililive-core && cargo test 18 | cd ./bililive-core && cargo test --no-default-features --features async-std 19 | cd ./bililive && cargo test 20 | cd ./bililive && cargo test --no-default-features --features async-native-tls 21 | cd ./actix-bililive && cargo test 22 | 23 | test-full: 24 | cd ./bililive-core && cargo test 25 | cd ./bililive-core && cargo test --no-default-features --features async-std 26 | cd ./bililive && cargo test 27 | cd ./bililive && cargo test --no-default-features --features async-native-tls 28 | cd ./actix-bililive && cargo test 29 | 30 | doc crate: 31 | cd "./{{crate}}" && cargo doc --all-features 32 | 33 | doc-all: (doc "bililive-core") (doc "bililive") (doc "actix-bililive") 34 | 35 | open crate: (doc crate) 36 | xdg-open "./target/doc/{{replace(crate, "-", "_")}}/index.html" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 lightquantum 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bililive-rs 2 | 3 | [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/PhotonQuantum/bililive-rs/Test?style=flat-square)](https://github.com/PhotonQuantum/bililive-rs/actions/workflows/test.yml) 4 | 5 | Simple stream-based bilibili live client libraries. 6 | 7 | ## Crates 8 | 9 | ### Core 10 | 11 | - [bililive-core](bililive-core) - Core traits and structs for a simple stream-based bilibili live danmaku implementation. 12 | 13 | ### Implementations 14 | 15 | - [bililive](bililive) - A simple stream-based bilibili live client library backed by [async-tungstenite](https://github.com/sdroege/async-tungstenite). Supports both tokio and async-std. 16 | - [actix-bililive](actix-bililive) - A simple stream-based bilibili live client library for the Actix ecosystem, backed by [awc](https://github.com/actix/actix-web/tree/master/awc). 17 | 18 | ## Features 19 | 20 | - Ergonomic `Stream`/`Sink` interface. 21 | - Easy establishment of connection via given live room id. 22 | - Handles heartbeat packets automatically. 23 | - Auto retry when connection fails (optional). 24 | - Decompresses `Zlib` payloads automatically. 25 | 26 | ## License 27 | 28 | This project is licensed under [MIT License](LICENSE). -------------------------------------------------------------------------------- /actix-bililive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "actix-bililive" 3 | version = "0.1.0-beta.8" 4 | authors = ["LightQuantum "] 5 | edition = "2021" 6 | description = "A simple stream-based bilibili live client library for the Actix ecosystem." 7 | license = "MIT" 8 | keywords = ["bilibili", "live", "stream", "actix", "danmaku"] 9 | repository = "https://github.com/PhotonQuantum/bililive-rs" 10 | readme = "README.md" 11 | 12 | [package.metadata.docs.rs] 13 | all-features = true 14 | 15 | [features] 16 | default = ["openssl"] 17 | openssl = ["awc/openssl"] 18 | rustls = ["awc/rustls"] 19 | 20 | [dependencies] 21 | actix-codec = "0.5" 22 | awc = "3.4.0" 23 | bililive-core = { version = "0.1.0-beta.3", path = "../bililive-core", features = ["not-send"] } 24 | bytes = "1.5" 25 | futures = "0.3" 26 | log = "0.4" 27 | serde = "1.0" 28 | stream-reconnect = { version = "0.4.0-beta.4", features = ["not-send"] } 29 | 30 | [dev-dependencies] 31 | actix-rt = "2.9" 32 | serde_json = "1.0" 33 | -------------------------------------------------------------------------------- /actix-bililive/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 lightquantum 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /actix-bililive/README.md: -------------------------------------------------------------------------------- 1 | # actix-bililive 2 | 3 | [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/PhotonQuantum/bililive-rs/Test?style=flat-square)](https://github.com/PhotonQuantum/bililive-rs/actions/workflows/test.yml) 4 | [![crates.io](https://img.shields.io/crates/v/actix-bililive?style=flat-square)](https://crates.io/crates/actix-bililive) 5 | [![Documentation](https://img.shields.io/docsrs/actix-bililive?style=flat-square)](https://docs.rs/actix-bililive) 6 | 7 | A simple stream-based bilibili live client library for the Actix ecosystem. 8 | 9 | *Minimum supported rust version: 1.56.0* 10 | 11 | ## Runtime Support 12 | 13 | This crate supports `actix-rt` (single-threaded `tokio`) runtime. 14 | 15 | ## Features 16 | 17 | - Ergonomic `Stream`/`Sink` interface. 18 | - Easy establishment of connection via given live room id. 19 | - Handles heartbeat packets automatically. 20 | - Auto retry when connection fails (optional). 21 | - Decompresses `Zlib` payloads automatically. 22 | 23 | ## Example 24 | 25 | ```rust 26 | use actix_bililive::{ConfigBuilder, RetryConfig, connect_with_retry}; 27 | 28 | use futures::StreamExt; 29 | use log::info; 30 | use serde_json::Value; 31 | 32 | let config = ConfigBuilder::new() 33 | .by_uid(1602085) 34 | .await 35 | .unwrap() 36 | .fetch_conf() 37 | .await 38 | .unwrap() 39 | .build(); 40 | 41 | let mut stream = connect_with_retry(config, RetryConfig::default()).await.unwrap(); 42 | while let Some(e) = stream.next().await { 43 | match e { 44 | Ok(packet) => { 45 | info!("raw: {:?}", packet); 46 | if let Ok(json) = packet.json::() { 47 | info!("json: {:?}", json); 48 | } 49 | } 50 | Err(e) => { 51 | info!("err: {:?}", e); 52 | } 53 | } 54 | } 55 | ``` -------------------------------------------------------------------------------- /actix-bililive/examples/simple.rs: -------------------------------------------------------------------------------- 1 | use futures::StreamExt; 2 | use log::info; 3 | use serde_json::Value; 4 | 5 | use actix_bililive::{connect_with_retry, ConfigBuilder, RetryConfig}; 6 | 7 | #[actix_rt::main] 8 | async fn main() { 9 | let config = ConfigBuilder::new() 10 | .by_uid(1602085) 11 | .await 12 | .unwrap() 13 | .fetch_conf() 14 | .await 15 | .unwrap() 16 | .build(); 17 | 18 | let mut stream = connect_with_retry(config, RetryConfig::default()) 19 | .await 20 | .unwrap(); 21 | while let Some(e) = stream.next().await { 22 | match e { 23 | Ok(packet) => { 24 | info!("raw: {:?}", packet); 25 | if let Ok(json) = packet.json::() { 26 | info!("json: {:?}", json); 27 | } 28 | } 29 | Err(e) => { 30 | info!("err: {:?}", e); 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /actix-bililive/src/builder/awc.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::pin::Pin; 3 | use std::str::FromStr; 4 | 5 | use awc::http::Uri; 6 | use awc::Client; 7 | use serde::de::DeserializeOwned; 8 | 9 | use crate::core::builder::Requester; 10 | 11 | type BoxedError = Box; 12 | 13 | #[derive(Default)] 14 | pub struct AWCClient(Client); 15 | 16 | impl From for AWCClient { 17 | fn from(client: Client) -> Self { 18 | Self(client) 19 | } 20 | } 21 | 22 | impl Requester for AWCClient { 23 | fn get_json( 24 | &self, 25 | url: &str, 26 | ) -> Pin> + '_>> { 27 | let url = Uri::from_str(url).unwrap(); 28 | Box::pin(async move { Ok(self.0.get(url).send().await?.json().await?) }) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /actix-bililive/src/builder/mod.rs: -------------------------------------------------------------------------------- 1 | mod awc; 2 | #[cfg(test)] 3 | pub(crate) mod tests; 4 | 5 | /// `bililive` stream config builder. 6 | /// 7 | /// Stream config can be built via given live room parameters (room id and user id) & danmaku server configs (server token and list). 8 | /// 9 | /// See the generic type [`ConfigBuilder`](bililive_core::builder::ConfigBuilder) for details. 10 | /// 11 | /// # Helper methods 12 | /// 13 | /// [`by_uid`](ConfigBuilder::by_uid) fetches room id by given user id. 14 | /// 15 | /// [`fetch_conf`](ConfigBuilder::fetch_conf) fetches danmaku server token and list without any input parameter. 16 | pub type ConfigBuilder = 17 | bililive_core::builder::ConfigBuilder; 18 | -------------------------------------------------------------------------------- /actix-bililive/src/builder/tests.rs: -------------------------------------------------------------------------------- 1 | use crate::core::config::StreamConfig; 2 | 3 | use super::ConfigBuilder; 4 | 5 | pub(crate) async fn build_real_config(override_servers: bool) -> StreamConfig { 6 | let builder = ConfigBuilder::new() 7 | .by_uid(419220) 8 | .await 9 | .expect("unable to fetch room_id") 10 | .fetch_conf() 11 | .await 12 | .expect("unable to fetch server conf"); 13 | let builder = if override_servers { 14 | builder.servers(&["wss://broadcastlv.chat.bilibili.com/sub".to_string()]) 15 | } else { 16 | builder 17 | }; 18 | builder.build() 19 | } 20 | 21 | #[actix_rt::test] 22 | async fn must_build_real_config_tokio() { 23 | build_real_config(false).await; 24 | } 25 | -------------------------------------------------------------------------------- /actix-bililive/src/connect.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::pin::Pin; 3 | use std::str::FromStr; 4 | 5 | use actix_codec::Framed; 6 | use awc::error::WsClientError; 7 | use awc::http::{Uri, Version}; 8 | use awc::{BoxedSocket, Client}; 9 | use stream_reconnect::{ReconnectStream, UnderlyingStream}; 10 | 11 | use crate::core::config::StreamConfig; 12 | use crate::core::errors::StreamError; 13 | use crate::core::packet::Packet; 14 | use crate::core::retry::{RetryConfig, RetryContext, WsStream, WsStreamTrait}; 15 | use crate::core::stream::HeartbeatStream; 16 | use crate::stream::{Codec, PingPongStream}; 17 | 18 | /// Raw websocket stream type. 19 | pub type InnerStream = PingPongStream>; 20 | /// Bililive stream type. 21 | pub type DefaultStream = HeartbeatStream; 22 | /// Bililive stream type with auto-reconnect mechanism. 23 | pub type RetryStream = ReconnectStream< 24 | WsStream, 25 | RetryContext, 26 | Result>, 27 | StreamError, 28 | >; 29 | 30 | pub struct Connector; 31 | 32 | impl WsStreamTrait for Connector { 33 | type Stream = DefaultStream; 34 | fn connect( 35 | url: &str, 36 | ) -> Pin> + '_>> { 37 | let client = Client::builder() 38 | .max_http_version(Version::HTTP_11) 39 | .finish(); 40 | let url = Uri::from_str(url).unwrap(); 41 | Box::pin(async move { 42 | let (_, ws) = client.ws(url).connect().await?; 43 | let codec = ws.into_map_codec(Codec::new); 44 | Ok(HeartbeatStream::new(PingPongStream::new(codec))) 45 | }) 46 | } 47 | } 48 | 49 | /// Connect to bilibili live room. 50 | /// 51 | /// # Errors 52 | /// Returns an error when websocket connection fails. 53 | pub async fn connect(config: StreamConfig) -> Result> { 54 | WsStream::::establish(config.into()).await 55 | } 56 | 57 | /// Connect to bilibili live room with auto retry. 58 | /// 59 | /// # Errors 60 | /// Returns an error when websocket connection fails. 61 | pub async fn connect_with_retry( 62 | stream_config: StreamConfig, 63 | retry_config: RetryConfig, 64 | ) -> Result> { 65 | let inner: RetryStream = 66 | ReconnectStream::connect_with_options(stream_config.into(), retry_config.into()).await?; 67 | Ok(inner) 68 | } 69 | -------------------------------------------------------------------------------- /actix-bililive/src/errors.rs: -------------------------------------------------------------------------------- 1 | //! Error types. 2 | use awc::error::WsClientError; 3 | 4 | pub use crate::core::errors::{BuildError, IncompleteResult, ParseError}; 5 | 6 | /// Errors that may occur when consuming a stream. 7 | pub type StreamError = crate::core::errors::StreamError; 8 | -------------------------------------------------------------------------------- /actix-bililive/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A simple stream-based bilibili live client library for the Actix ecosystem. 2 | //! 3 | //! *Minimum supported rust version: 1.56.0* 4 | //! 5 | //! ## Runtime Support 6 | //! 7 | //! This crate supports `actix-rt` (single-threaded `tokio`) runtime. 8 | //! 9 | //! ## Features 10 | //! 11 | //! - Ergonomic `Stream`/`Sink` interface. 12 | //! - Easy establishment of connection via given live room id. 13 | //! - Handles heartbeat packets automatically. 14 | //! - Auto retry when connection fails (optional). 15 | //! - Decompresses `Zlib` payloads automatically. 16 | //! 17 | //! ## Example 18 | //! 19 | //! ```rust 20 | //! use actix_bililive::{ConfigBuilder, RetryConfig, connect_with_retry}; 21 | //! 22 | //! use futures::StreamExt; 23 | //! use log::info; 24 | //! use serde_json::Value; 25 | //! 26 | //! # async fn test() { 27 | //! let config = ConfigBuilder::new() 28 | //! .by_uid(1602085) 29 | //! .await 30 | //! .unwrap() 31 | //! .fetch_conf() 32 | //! .await 33 | //! .unwrap() 34 | //! .build(); 35 | //! 36 | //! let mut stream = connect_with_retry(config, RetryConfig::default()).await.unwrap(); 37 | //! while let Some(e) = stream.next().await { 38 | //! match e { 39 | //! Ok(packet) => { 40 | //! info!("raw: {:?}", packet); 41 | //! if let Ok(json) = packet.json::() { 42 | //! info!("json: {:?}", json); 43 | //! } 44 | //! } 45 | //! Err(e) => { 46 | //! info!("err: {:?}", e); 47 | //! } 48 | //! } 49 | //! } 50 | //! # 51 | //! # } 52 | //! ``` 53 | 54 | #![allow(clippy::module_name_repetitions, clippy::future_not_send)] 55 | 56 | pub use bililive_core as core; 57 | #[doc(inline)] 58 | pub use builder::ConfigBuilder; 59 | pub use connect::{connect, connect_with_retry}; 60 | 61 | pub use crate::core::packet::*; 62 | pub use crate::core::retry::RetryConfig; 63 | 64 | mod builder; 65 | mod connect; 66 | pub mod errors; 67 | pub mod stream; 68 | -------------------------------------------------------------------------------- /actix-bililive/src/stream/codec.rs: -------------------------------------------------------------------------------- 1 | use actix_codec::{Decoder, Encoder}; 2 | use awc::error::WsClientError; 3 | use awc::ws::Codec as WsCodec; 4 | use awc::ws::{Frame, Message}; 5 | use bytes::BytesMut; 6 | use log::{debug, warn}; 7 | 8 | use crate::core::errors::{IncompleteResult, StreamError}; 9 | use crate::core::packet::Packet; 10 | 11 | use super::PacketOrPing; 12 | 13 | /// Bililive protocol codec. 14 | #[derive(Debug)] 15 | pub struct Codec { 16 | ws_codec: WsCodec, 17 | read_buffer: Vec, 18 | } 19 | 20 | impl Codec { 21 | /// Construct a new bililive codec with given websocket protocol codec. 22 | #[must_use] 23 | pub const fn new(ws_codec: WsCodec) -> Self { 24 | Self { 25 | ws_codec, 26 | read_buffer: vec![], 27 | } 28 | } 29 | } 30 | 31 | impl Decoder for Codec { 32 | type Item = PacketOrPing; 33 | type Error = StreamError; 34 | 35 | fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { 36 | let ws_frame = if let Some(frame) = self 37 | .ws_codec 38 | .decode(src) 39 | .map_err(|e| StreamError::from_ws_error(e.into()))? 40 | { 41 | frame 42 | } else { 43 | return Ok(None); 44 | }; 45 | 46 | match ws_frame { 47 | Frame::Binary(bytes) => { 48 | self.read_buffer.extend_from_slice(&bytes); 49 | 50 | match Packet::parse(&self.read_buffer) { 51 | IncompleteResult::Ok((remaining, pack)) => { 52 | debug!("packet parsed, {} bytes remaining", remaining.len()); 53 | 54 | // remove parsed bytes 55 | let consume_len = self.read_buffer.len() - remaining.len(); 56 | drop(self.read_buffer.drain(..consume_len)); 57 | 58 | Ok(Some(pack.into())) 59 | } 60 | IncompleteResult::Incomplete(needed) => { 61 | debug!("incomplete packet, {:?} needed", needed); 62 | Ok(None) 63 | } 64 | IncompleteResult::Err(e) => { 65 | warn!("error occurred when parsing incoming packet"); 66 | Err(e.into()) 67 | } 68 | } 69 | } 70 | Frame::Ping(bytes) => { 71 | debug!("incoming ws ping"); 72 | Ok(Some(PacketOrPing::PingPong(bytes))) 73 | } 74 | _ => { 75 | debug!("not a binary message, dropping"); 76 | Ok(None) 77 | } 78 | } 79 | } 80 | } 81 | 82 | impl Encoder for Codec { 83 | type Error = StreamError; 84 | 85 | fn encode(&mut self, item: PacketOrPing, dst: &mut BytesMut) -> Result<(), Self::Error> { 86 | let msg = match item { 87 | PacketOrPing::Packet(pack) => Message::Binary(pack.encode().into()), 88 | PacketOrPing::PingPong(bytes) => Message::Pong(bytes), 89 | }; 90 | self.ws_codec 91 | .encode(msg, dst) 92 | .map_err(|e| StreamError::from_ws_error(e.into())) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /actix-bililive/src/stream/mod.rs: -------------------------------------------------------------------------------- 1 | //! Bilibili live stream. 2 | 3 | use bytes::Bytes; 4 | 5 | pub use codec::Codec; 6 | pub use pingpong::PingPongStream; 7 | 8 | use crate::core::packet::Packet; 9 | 10 | mod codec; 11 | mod pingpong; 12 | #[cfg(test)] 13 | mod tests; 14 | 15 | /// Either a valid bililive packet or a websocket ping message. 16 | #[derive(Debug)] 17 | pub enum PacketOrPing { 18 | Packet(Packet), 19 | PingPong(Bytes), 20 | } 21 | 22 | impl From for PacketOrPing { 23 | fn from(pack: Packet) -> Self { 24 | Self::Packet(pack) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /actix-bililive/src/stream/pingpong.rs: -------------------------------------------------------------------------------- 1 | use std::pin::Pin; 2 | use std::sync::Arc; 3 | use std::task::Waker; 4 | use std::task::{Context, Poll}; 5 | 6 | use awc::error::WsClientError; 7 | use futures::Stream; 8 | use futures::{ready, Sink}; 9 | use log::debug; 10 | 11 | use crate::core::errors::StreamError; 12 | use crate::core::packet::Packet; 13 | use crate::core::stream::waker::WakerProxy; 14 | 15 | use super::PacketOrPing; 16 | 17 | /// Auto websocket ping responder wrapper. 18 | pub struct PingPongStream { 19 | stream: T, 20 | tx_waker: Arc, 21 | } 22 | 23 | impl PingPongStream { 24 | pub fn new(stream: T) -> Self { 25 | Self { 26 | stream, 27 | tx_waker: Arc::new(WakerProxy::default()), 28 | } 29 | } 30 | 31 | fn with_context(&mut self, f: F) -> U 32 | where 33 | F: FnOnce(&mut Context<'_>, &mut T) -> U, 34 | { 35 | let waker = Waker::from(self.tx_waker.clone()); 36 | let mut cx = Context::from_waker(&waker); 37 | 38 | f(&mut cx, &mut self.stream) 39 | } 40 | } 41 | 42 | impl Stream for PingPongStream 43 | where 44 | T: Stream>> 45 | + Sink> 46 | + Unpin, 47 | { 48 | type Item = Result>; 49 | 50 | fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 51 | // register current task to be waken on poll_ready 52 | self.tx_waker.rx(cx.waker()); 53 | 54 | // ensure that all pending write op are completed 55 | ready!(self.with_context(|cx, s| Pin::new(s).poll_ready(cx)))?; 56 | 57 | match ready!(Pin::new(&mut self.stream).poll_next(cx)) { 58 | Some(Ok(PacketOrPing::PingPong(bytes))) => { 59 | // we need to send pong, so push it into the sink 60 | debug!("sending pong"); 61 | Pin::new(&mut self.stream).start_send(PacketOrPing::PingPong(bytes))?; 62 | 63 | // ensure that pong is sent 64 | let _ = self.with_context(|cx, s| Pin::new(s).poll_flush(cx))?; 65 | 66 | Poll::Pending 67 | } 68 | Some(Ok(PacketOrPing::Packet(pack))) => Poll::Ready(Some(Ok(pack))), 69 | Some(Err(e)) => Poll::Ready(Some(Err(e))), 70 | None => Poll::Ready(None), 71 | } 72 | } 73 | } 74 | 75 | impl Sink for PingPongStream 76 | where 77 | T: Sink> + Unpin, 78 | { 79 | type Error = StreamError; 80 | 81 | fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 82 | // wake current task and stream task 83 | self.tx_waker.tx(cx.waker()); 84 | 85 | // poll the underlying websocket sink 86 | self.with_context(|cx, s| Pin::new(s).poll_ready(cx)) 87 | } 88 | 89 | fn start_send(mut self: Pin<&mut Self>, item: Packet) -> Result<(), Self::Error> { 90 | Pin::new(&mut self.stream).start_send(item.into()) 91 | } 92 | 93 | fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 94 | // wake current task and stream task 95 | self.tx_waker.tx(cx.waker()); 96 | 97 | // poll the underlying websocket sink 98 | self.with_context(|cx, s| Pin::new(s).poll_flush(cx)) 99 | } 100 | 101 | fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 102 | // wake current task and stream task 103 | self.tx_waker.tx(cx.waker()); 104 | 105 | // poll the underlying websocket sink 106 | self.with_context(|cx, s| Pin::new(s).poll_close(cx)) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /actix-bililive/src/stream/tests.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use awc::error::WsClientError; 4 | use futures::{Future, Sink, SinkExt, Stream, StreamExt}; 5 | 6 | use crate::builder::tests::build_real_config; 7 | use crate::core::errors::StreamError; 8 | use crate::core::packet::{Operation, Packet, Protocol}; 9 | use crate::core::retry::RetryConfig; 10 | 11 | async fn must_future_timeout(dur: Duration, fut: impl Future) { 12 | assert!( 13 | actix_rt::time::timeout(dur, fut).await.is_err(), 14 | "future not timeout" 15 | ); 16 | } 17 | 18 | async fn test_stream( 19 | mut stream: impl Stream>> 20 | + Sink> 21 | + Unpin, 22 | ) { 23 | let mut msg_count = 0; 24 | 25 | let stream_try = async { 26 | while let Some(msg) = stream.next().await { 27 | msg.expect("stream error"); 28 | msg_count += 1; 29 | } 30 | }; 31 | // err means timeout indicating there's no early stop on stream 32 | must_future_timeout(Duration::from_secs(3), stream_try).await; 33 | 34 | stream 35 | .send(Packet::new(Operation::HeartBeat, Protocol::Json, vec![])) 36 | .await 37 | .expect("sink error"); 38 | let mut hb_resp_received = false; 39 | let stream_try = async { 40 | while let Some(msg) = stream.next().await { 41 | let msg = msg.expect("stream error"); 42 | if msg.op() == Operation::HeartBeatResponse { 43 | hb_resp_received = true; 44 | } 45 | } 46 | }; 47 | // err means timeout indicating there's no early stop on stream 48 | must_future_timeout(Duration::from_secs(1), stream_try).await; 49 | assert!(hb_resp_received, "no heart beat response received"); 50 | 51 | stream.close().await.expect("unable to close stream"); 52 | } 53 | 54 | async fn test_stream_heartbeat( 55 | mut stream: impl Stream>> 56 | + Sink> 57 | + Unpin, 58 | ) { 59 | let stream_try = async { 60 | while let Some(Ok(_)) = stream.next().await {} 61 | panic!("connection closed (heartbeat not sent)"); 62 | }; 63 | // err means timeout indicating there's no early stop on stream 64 | must_future_timeout(Duration::from_secs(120), stream_try).await; 65 | 66 | stream.close().await.expect("unable to close stream"); 67 | } 68 | 69 | #[actix_rt::test] 70 | async fn must_stream_tokio() { 71 | let config = build_real_config(true).await; 72 | 73 | let stream = crate::connect::connect(config) 74 | .await 75 | .expect("unable to establish connection"); 76 | test_stream(stream).await; 77 | } 78 | 79 | #[actix_rt::test] 80 | async fn must_retry_stream_tokio() { 81 | let config = build_real_config(false).await; 82 | 83 | let stream = crate::connect::connect_with_retry(config, RetryConfig::default()) 84 | .await 85 | .expect("unable to establish connection"); 86 | test_stream(stream).await; 87 | } 88 | 89 | #[actix_rt::test] 90 | async fn must_hb_tokio() { 91 | if option_env!("FAST_TEST").is_some() { 92 | return; 93 | } 94 | 95 | let config = build_real_config(true).await; 96 | 97 | let stream = crate::connect::connect(config) 98 | .await 99 | .expect("unable to establish connection"); 100 | test_stream_heartbeat(stream).await; 101 | } 102 | -------------------------------------------------------------------------------- /bililive-core/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "bililive-core" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /bililive-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bililive-core" 3 | version = "0.1.0-beta.4" 4 | edition = "2021" 5 | authors = ["LightQuantum "] 6 | description = "Core traits and structs for a simple stream-based bilibili live danmaku implementation." 7 | license = "MIT" 8 | keywords = ["bilibili", "live", "stream", "core", "danmaku"] 9 | repository = "https://github.com/PhotonQuantum/bililive-rs" 10 | readme = "README.md" 11 | 12 | [package.metadata.docs.rs] 13 | all-features = true 14 | 15 | [features] 16 | default = ["tokio"] 17 | tokio = ["tokio1", "stream-reconnect/tokio"] 18 | async-std = ["async-std1", "stream-reconnect/async-std"] 19 | not-send = ["stream-reconnect/not-send"] 20 | 21 | [dependencies] 22 | async-std1 = { package = "async-std", version = "1.10", optional = true } 23 | flate2 = "1.0" 24 | futures = "0.3" 25 | log = "0.4" 26 | nom = "7.1" 27 | rand = "0.8" 28 | serde = { version = "1.0", features = ["derive"] } 29 | serde_json = "1.0" 30 | stream-reconnect = { version = "0.4.0-beta.4", default-features = false } 31 | thiserror = "1.0" 32 | tokio1 = { package = "tokio", version = "1.13", features = ["rt"], optional = true } 33 | url = { version = "2.5", features = ["serde"] } -------------------------------------------------------------------------------- /bililive-core/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 lightquantum 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /bililive-core/README.md: -------------------------------------------------------------------------------- 1 | # bililive-core 2 | 3 | [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/PhotonQuantum/bililive-rs/Test?style=flat-square)](https://github.com/PhotonQuantum/bililive-rs/actions/workflows/test.yml) 4 | [![crates.io](https://img.shields.io/crates/v/bililive-core?style=flat-square)](https://crates.io/crates/bililive-core) 5 | [![Documentation](https://img.shields.io/docsrs/bililive-core?style=flat-square)](https://docs.rs/bililive-core) 6 | 7 | A simple stream-based bilibili live danmaku implementation for Rust. 8 | 9 | This crate contains core traits, types and parsing implementations needed to build a 10 | complete bilibili live client. 11 | 12 | If you need a batteries-included client, you may want to look at `bililive` or `actix-bililive`. 13 | 14 | ## Feature Flags 15 | - `tokio` (default) - enable tokio support. 16 | - `async-std` - enable async-std support. 17 | - `not-send` - Remove `Send` constraints on traits and types. Useful for actix clients. -------------------------------------------------------------------------------- /bililive-core/src/builder/mod.rs: -------------------------------------------------------------------------------- 1 | //! `bililive` config builder. 2 | 3 | use std::future::Future; 4 | use std::marker::PhantomData; 5 | use std::pin::Pin; 6 | 7 | /// `bililive` stream config builder. 8 | /// 9 | /// Stream config can be built via given live room parameters (room id and user id) & danmaku server configs (server token and list). 10 | /// 11 | /// # Helper methods 12 | /// 13 | /// [`by_uid`](ConfigBuilder::by_uid) fetches room id by given user id. 14 | /// 15 | /// [`fetch_conf`](ConfigBuilder::fetch_conf) fetches danmaku server token and list without any input parameter. 16 | /// 17 | /// See docs of downstream crates for details. 18 | use serde::de::DeserializeOwned; 19 | 20 | use crate::builder::types::{ConfQueryInner, Resp, RoomQueryInner}; 21 | use crate::config::StreamConfig; 22 | use crate::errors::{BoxedError, BuildError}; 23 | 24 | #[cfg(test)] 25 | mod tests; 26 | mod types; 27 | 28 | /// An abstract HTTP client. 29 | /// 30 | /// Used in [`ConfigBuilder`](ConfigBuilder) to help fetching bilibili config. 31 | #[cfg(feature = "not-send")] 32 | pub trait Requester { 33 | /// Make a `GET` request to the url and try to deserialize the response body as JSON. 34 | fn get_json( 35 | &self, 36 | url: &str, 37 | ) -> Pin> + '_>>; 38 | } 39 | 40 | /// An abstract HTTP client. 41 | /// 42 | /// Used in [`ConfigBuilder`](ConfigBuilder) to help fetching bilibili config. 43 | #[cfg(not(feature = "not-send"))] 44 | pub trait Requester: Send + Sync { 45 | /// Make a `GET` request to the url and try to deserialize the response body as JSON. 46 | fn get_json( 47 | &self, 48 | url: &str, 49 | ) -> Pin> + Send + '_>>; 50 | } 51 | 52 | #[doc(hidden)] 53 | pub enum BF {} 54 | 55 | #[doc(hidden)] 56 | pub enum BN {} 57 | 58 | /// `bililive` stream config builder. 59 | /// 60 | /// Stream config can be built via given live room parameters (room id and user id) & danmaku server configs (server token and list). 61 | /// 62 | /// # Helper methods 63 | /// 64 | /// [`by_uid`](ConfigBuilder::by_uid) fetches room id by given user id. 65 | /// 66 | /// [`fetch_conf`](ConfigBuilder::fetch_conf) fetches danmaku server token and list without any input parameter. 67 | #[derive(Debug)] 68 | pub struct ConfigBuilder { 69 | http: H, 70 | room_id: Option, 71 | uid: Option, 72 | token: Option, 73 | servers: Option>, 74 | __marker: PhantomData<(R, U, T, S)>, 75 | } 76 | 77 | impl ConfigBuilder { 78 | /// Construct a new builder with default requester client. 79 | #[allow(clippy::new_without_default)] 80 | #[must_use] 81 | pub fn new() -> Self { 82 | Self::new_with_client(H::default()) 83 | } 84 | } 85 | 86 | impl ConfigBuilder { 87 | /// Construct a new builder with given requester client. 88 | #[must_use] 89 | pub const fn new_with_client(client: H) -> Self { 90 | Self { 91 | http: client, 92 | room_id: None, 93 | uid: None, 94 | token: None, 95 | servers: None, 96 | __marker: PhantomData, 97 | } 98 | } 99 | } 100 | 101 | impl ConfigBuilder { 102 | #[allow(clippy::missing_const_for_fn)] // misreport 103 | fn cast(self) -> ConfigBuilder { 104 | ConfigBuilder { 105 | http: self.http, 106 | room_id: self.room_id, 107 | uid: self.uid, 108 | token: self.token, 109 | servers: self.servers, 110 | __marker: PhantomData, 111 | } 112 | } 113 | } 114 | 115 | impl ConfigBuilder { 116 | #[must_use] 117 | pub fn room_id(mut self, room_id: u64) -> ConfigBuilder { 118 | self.room_id = Some(room_id); 119 | self.cast() 120 | } 121 | #[must_use] 122 | pub fn uid(mut self, uid: u64) -> ConfigBuilder { 123 | self.uid = Some(uid); 124 | self.cast() 125 | } 126 | #[must_use] 127 | pub fn token(mut self, token: &str) -> ConfigBuilder { 128 | self.token = Some(token.to_string()); 129 | self.cast() 130 | } 131 | 132 | #[must_use] 133 | pub fn servers(mut self, servers: &[String]) -> ConfigBuilder { 134 | self.servers = Some(servers.to_vec()); 135 | self.cast() 136 | } 137 | } 138 | 139 | impl ConfigBuilder 140 | where 141 | H: Requester, 142 | R: Send + Sync, 143 | U: Send + Sync, 144 | T: Send + Sync, 145 | S: Send + Sync, 146 | { 147 | /// Fills `room_id` and `uid` by given `uid`, fetching `room_id` automatically. 148 | /// 149 | /// # Errors 150 | /// Returns an error when HTTP api request fails. 151 | pub async fn by_uid(mut self, uid: u64) -> Result, BuildError> { 152 | let resp: Resp = self 153 | .http 154 | .get_json(&*format!( 155 | "https://api.live.bilibili.com/bili/living_v2/{}", 156 | uid 157 | )) 158 | .await 159 | .map_err(BuildError)?; 160 | let room_id = resp.room_id(); 161 | 162 | self.room_id = Some(room_id); 163 | self.uid = Some(uid); 164 | Ok(self.cast()) 165 | } 166 | 167 | /// Fetches danmaku server configs & uris 168 | /// 169 | /// # Errors 170 | /// Returns an error when HTTP api request fails. 171 | pub async fn fetch_conf(mut self) -> Result, BuildError> { 172 | let resp: Resp = self 173 | .http 174 | .get_json("https://api.live.bilibili.com/room/v1/Danmu/getConf") 175 | .await 176 | .map_err(BuildError)?; 177 | 178 | self.token = Some(resp.token().to_string()); 179 | self.servers = Some(resp.servers()); 180 | Ok(self.cast()) 181 | } 182 | } 183 | 184 | impl ConfigBuilder { 185 | /// Consumes the builder and returns [`StreamConfig`](StreamConfig) 186 | #[allow(clippy::missing_panics_doc)] 187 | pub fn build(self) -> StreamConfig { 188 | // SAFETY ensured by type state 189 | StreamConfig::new( 190 | self.room_id.unwrap(), 191 | self.uid.unwrap(), 192 | self.token.unwrap(), 193 | self.servers.unwrap(), 194 | ) 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /bililive-core/src/builder/tests.rs: -------------------------------------------------------------------------------- 1 | use crate::builder::ConfigBuilder; 2 | 3 | use super::types::{ConfQueryInner, Resp, RoomQueryInner}; 4 | 5 | #[test] 6 | fn must_parse_room_id() { 7 | let data = r#"{"code":0,"msg":"","message":"","data":{"status":0,"url":"https://live.bilibili.com/1016"}}"#; 8 | let parsed: Resp = 9 | serde_json::from_str(data).expect("unable to parse response"); 10 | assert_eq!(parsed.room_id(), 1016); 11 | } 12 | 13 | #[test] 14 | fn must_parse_conf() { 15 | let data = include_str!("../../tests/getConf.json"); 16 | let parsed: Resp = 17 | serde_json::from_str(data).expect("unable to parse response"); 18 | assert_eq!( 19 | parsed.token(), 20 | "zRLe_Wb0lwdalke2_OMvIxBD7uBQ7pNKepn-fP2rIV91AyCRSAYwsw1CVYGgjtuf8IA1AHLchDXhiekQ3IMWnzBu5zqIK9CqdY-tuaCpVi1fxE_hqBEdsfdgxPJyFQAxtgqK4cdf1dm7" 21 | ); 22 | assert_eq!( 23 | parsed.servers(), 24 | [ 25 | "wss://tx-gz-live-comet-03.chat.bilibili.com:443/sub", 26 | "wss://tx-sh-live-comet-03.chat.bilibili.com:443/sub", 27 | "wss://broadcastlv.chat.bilibili.com:443/sub" 28 | ] 29 | ) 30 | } 31 | 32 | #[test] 33 | fn must_build_config() { 34 | ConfigBuilder::<(), _, _, _, _>::new() 35 | .room_id(1016) 36 | .uid(0) 37 | .servers(&["wss://".to_string()]) 38 | .token("asdf") 39 | .build(); 40 | } 41 | -------------------------------------------------------------------------------- /bililive-core/src/builder/types.rs: -------------------------------------------------------------------------------- 1 | use serde::Deserialize; 2 | use url::Url; 3 | 4 | #[derive(Clone, Eq, PartialEq, Deserialize, Hash)] 5 | pub struct Resp { 6 | data: T, 7 | } 8 | 9 | impl Resp { 10 | pub fn token(&self) -> &str { 11 | &self.data.token 12 | } 13 | pub fn servers(&self) -> Vec { 14 | self.data 15 | .host_server_list 16 | .iter() 17 | .map(|server| format!("wss://{}:{}/sub", server.host, server.wss_port)) 18 | .collect() 19 | } 20 | } 21 | 22 | impl Resp { 23 | pub fn room_id(&self) -> u64 { 24 | let url = &self.data.url; 25 | assert_eq!(url.host_str().unwrap(), "live.bilibili.com"); 26 | url.path_segments() 27 | .into_iter() 28 | .flatten() 29 | .last() 30 | .unwrap() 31 | .parse() 32 | .unwrap() 33 | } 34 | } 35 | 36 | #[derive(Clone, Eq, PartialEq, Deserialize, Hash)] 37 | pub struct RoomQueryInner { 38 | url: Url, 39 | } 40 | 41 | #[derive(Clone, Eq, PartialEq, Deserialize, Hash)] 42 | pub struct ConfQueryInner { 43 | token: String, 44 | host_server_list: Vec, 45 | } 46 | 47 | #[derive(Clone, Eq, PartialEq, Deserialize, Hash)] 48 | struct WSServer { 49 | host: String, 50 | wss_port: u16, 51 | } 52 | -------------------------------------------------------------------------------- /bililive-core/src/config.rs: -------------------------------------------------------------------------------- 1 | //! Configuration types. 2 | 3 | /// The configuration for bilibili live stream connection. 4 | #[derive(Debug, Clone)] 5 | pub struct StreamConfig(Box); 6 | 7 | impl StreamConfig { 8 | #[must_use] 9 | pub fn new(room_id: u64, uid: u64, token: String, servers: Vec) -> Self { 10 | Self(Box::new(StreamConfigInner { 11 | room_id, 12 | uid, 13 | token, 14 | servers, 15 | })) 16 | } 17 | } 18 | 19 | impl StreamConfig { 20 | /// Live room id (long version). 21 | #[must_use] 22 | pub const fn room_id(&self) -> u64 { 23 | self.0.room_id 24 | } 25 | /// Live room user id. 26 | #[must_use] 27 | pub const fn uid(&self) -> u64 { 28 | self.0.uid 29 | } 30 | /// Danmaku server token. 31 | #[must_use] 32 | pub fn token(&self) -> &str { 33 | &self.0.token 34 | } 35 | /// Danmaku server urls. 36 | #[must_use] 37 | pub fn servers(&self) -> &[String] { 38 | &self.0.servers 39 | } 40 | } 41 | 42 | #[derive(Debug, Clone)] 43 | struct StreamConfigInner { 44 | /// Live room id (long version). 45 | room_id: u64, 46 | /// Live room user id. 47 | uid: u64, 48 | /// Danmaku server token. 49 | token: String, 50 | servers: Vec, 51 | } 52 | -------------------------------------------------------------------------------- /bililive-core/src/errors.rs: -------------------------------------------------------------------------------- 1 | //! Error types. 2 | use std::fmt::Debug; 3 | 4 | use nom::Needed; 5 | use thiserror::Error; 6 | 7 | /// The result returned by parsing functions. 8 | /// 9 | /// * `Ok` indicates a successful parse. 10 | /// * `Incomplete` means that more data is needed to complete the parsing. 11 | /// The `Needed` enum can contain how many additional bytes are necessary. 12 | /// * `Err` indicates an error. 13 | pub enum IncompleteResult { 14 | Ok(T), 15 | Incomplete(Needed), 16 | Err(ParseError), 17 | } 18 | 19 | /// Errors that may occur when parsing a packet. 20 | #[derive(Debug, Error)] 21 | pub enum ParseError { 22 | #[error("json error: {0}")] 23 | Json(#[from] serde_json::Error), 24 | #[error("not a valid int32 big endian")] 25 | Int32BE, 26 | #[error("unknown websocket pack protocol")] 27 | UnknownProtocol, 28 | #[error("error when parsing packet struct")] 29 | PacketError(String), 30 | #[error("error when decompressing packet buffer: {0}")] 31 | ZlibError(#[from] std::io::Error), 32 | } 33 | 34 | #[cfg(feature = "not-send")] 35 | pub(crate) type BoxedError = Box; 36 | 37 | #[cfg(not(feature = "not-send"))] 38 | pub(crate) type BoxedError = Box; 39 | 40 | /// Errors that may occur when making HTTP requests through builder. 41 | #[derive(Debug, Error)] 42 | #[error("error when making http request: {0}")] 43 | pub struct BuildError(#[source] pub(crate) BoxedError); 44 | 45 | /// Errors that may occur when consuming a stream. 46 | /// 47 | /// `E` is determined by the underlying websocket implementation. 48 | #[derive(Debug, Error)] 49 | pub enum StreamError { 50 | #[error("parse error: {0}")] 51 | Parse(#[from] ParseError), 52 | #[error("ws error: {0}")] 53 | WebSocket(E), 54 | #[error("io error: {0}")] 55 | IO(#[from] std::io::Error), 56 | } 57 | 58 | impl StreamError { 59 | pub const fn from_ws_error(e: E) -> Self { 60 | Self::WebSocket(e) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /bililive-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A simple stream-based bilibili live danmaku implementation for Rust. 2 | //! 3 | //! This crate contains core traits, types and parsing implementations needed to build a 4 | //! complete bilibili live client. 5 | //! 6 | //! If you need a batteries-included client, you may want to look at `bililive` or `actix-bililive`. 7 | //! 8 | //! ## Feature Flags 9 | //! - `tokio` (default) - enable tokio support. 10 | //! - `async-std` - enable async-std support. 11 | //! - `not-send` - Remove `Send` constraints on traits and types. Useful for actix clients. 12 | 13 | #![allow( 14 | clippy::cast_lossless, 15 | clippy::cast_possible_truncation, 16 | clippy::module_name_repetitions, 17 | clippy::default_trait_access 18 | )] 19 | 20 | pub mod builder; 21 | pub mod config; 22 | pub mod errors; 23 | pub mod packet; 24 | pub mod retry; 25 | pub mod stream; 26 | -------------------------------------------------------------------------------- /bililive-core/src/packet/mod.rs: -------------------------------------------------------------------------------- 1 | //! Packet types. 2 | 3 | use std::convert::TryInto; 4 | use std::io::{Cursor, Read, Write}; 5 | 6 | use flate2::read::ZlibDecoder; 7 | use flate2::write::ZlibEncoder; 8 | use flate2::Compression; 9 | use nom::Err; 10 | use serde::Deserialize; 11 | use serde_json::json; 12 | 13 | pub use types::*; 14 | 15 | use crate::config::StreamConfig; 16 | use crate::errors::{IncompleteResult, ParseError}; 17 | 18 | mod parser; 19 | mod types; 20 | 21 | #[cfg(test)] 22 | mod tests; 23 | 24 | type Result = std::result::Result; 25 | 26 | /// Bililive packet. 27 | /// 28 | /// Packet can be used to encode/parse raw bilibili live packets, and extract information from it. 29 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] 30 | pub struct Packet { 31 | packet_length: u32, 32 | header_length: u16, 33 | protocol_version: Protocol, 34 | op: Operation, 35 | seq_id: u32, 36 | data: Vec, 37 | } 38 | 39 | impl Packet { 40 | /// Set the protocol version. 41 | pub fn set_proto(&mut self, protocol_version: Protocol) { 42 | self.protocol_version = protocol_version; 43 | } 44 | /// Set the operation. 45 | pub fn set_op(&mut self, op: Operation) { 46 | self.op = op; 47 | } 48 | /// Set the sequence id. By default it's 1. 49 | pub fn set_seq_id(&mut self, seq_id: u32) { 50 | self.seq_id = seq_id; 51 | } 52 | /// Set the packet body. 53 | /// Packet length will be updated automatically. 54 | pub fn set_data>>(&mut self, data: T) { 55 | self.data = data.into(); 56 | self.packet_length = self.header_length as u32 + self.data.len() as u32; 57 | } 58 | } 59 | 60 | impl Packet { 61 | /// Construct a new packet. 62 | /// 63 | /// To construct a zlib-compressed packet, you should create a JSON/Int32BE packet first, 64 | /// then call [`Packet::compress`](Packet::compress) to convert it to a zlib one. 65 | pub fn new>>(op: Operation, protocol_version: Protocol, data: T) -> Self { 66 | let data = data.into(); 67 | 68 | Self { 69 | packet_length: data.len() as u32 + 16, 70 | header_length: 16, 71 | protocol_version, 72 | op, 73 | seq_id: 1, 74 | data, 75 | } 76 | } 77 | 78 | /// Convert a JSON/Int32BE packet to a zlib-compressed one. 79 | /// 80 | /// # Errors 81 | /// Return errors if compression fails. 82 | pub fn compress(self) -> Result { 83 | let raw = self.encode(); 84 | 85 | let mut z = ZlibEncoder::new(Vec::new(), Compression::default()); 86 | z.write_all(&raw)?; 87 | let data = z.finish()?; 88 | 89 | Ok(Self::new(self.op, Protocol::Zlib, data)) 90 | } 91 | } 92 | 93 | impl Packet { 94 | #[allow(clippy::missing_panics_doc)] 95 | #[must_use] 96 | pub fn new_room_enter(config: &StreamConfig) -> Self { 97 | Self::new( 98 | Operation::RoomEnter, 99 | Protocol::Json, 100 | serde_json::to_vec(&json!({ 101 | "uid": config.uid(), 102 | "roomid": config.room_id(), 103 | "protover": 2, 104 | "platform": "web", 105 | "clientver": "1.8.2", 106 | "type": 2, 107 | "key": config.token() 108 | })) 109 | .unwrap(), 110 | ) 111 | } 112 | } 113 | 114 | impl Packet { 115 | /// Get the packet length. 116 | #[must_use] 117 | pub const fn packet_length(&self) -> u32 { 118 | self.packet_length 119 | } 120 | /// Get the header length. 121 | #[must_use] 122 | pub const fn header_length(&self) -> u16 { 123 | self.header_length 124 | } 125 | /// Get the sequence id. 126 | #[must_use] 127 | pub const fn seq_id(&self) -> u32 { 128 | self.seq_id 129 | } 130 | /// Get the operation. 131 | #[must_use] 132 | pub const fn op(&self) -> Operation { 133 | self.op 134 | } 135 | /// Get the protocol version. 136 | #[must_use] 137 | pub const fn proto(&self) -> Protocol { 138 | self.protocol_version 139 | } 140 | /// Get bytes of the body. 141 | #[must_use] 142 | pub fn bytes(&self) -> &[u8] { 143 | &self.data 144 | } 145 | /// Try to parse the body by json. 146 | /// 147 | /// # Errors 148 | /// It may fail if the model is incorrect or it's not a json packet. 149 | /// You may check the type of the packet by [`Packet::proto`](Packet::proto). 150 | pub fn json<'a, T: Deserialize<'a>>(&'a self) -> Result { 151 | serde_json::from_slice(&self.data).map_err(ParseError::Json) 152 | } 153 | /// Try to parse the body by big endian int32. 154 | /// 155 | /// # Errors 156 | /// It may fail if it's not a int packet. 157 | /// You may check the type of the packet by [`Packet::proto`](Packet::proto). 158 | pub fn int32_be(&self) -> Result { 159 | Ok(i32::from_be_bytes( 160 | self.data 161 | .as_slice() 162 | .try_into() 163 | .map_err(|_| ParseError::Int32BE)?, 164 | )) 165 | } 166 | } 167 | 168 | impl Packet { 169 | /// Encode the packet into bytes ready to be sent to the server. 170 | #[must_use] 171 | pub fn encode(&self) -> Vec { 172 | let mut buf = Vec::with_capacity(self.packet_length as usize); 173 | buf.extend(self.packet_length.to_be_bytes()); 174 | buf.extend(self.header_length.to_be_bytes()); 175 | buf.extend((self.protocol_version as u16).to_be_bytes()); 176 | buf.extend((self.op as u32).to_be_bytes()); 177 | buf.extend(self.seq_id.to_be_bytes()); 178 | buf.extend(&self.data); 179 | buf 180 | } 181 | 182 | /// Parse the packet received from Bilibili server. 183 | #[must_use] 184 | pub fn parse(input: &[u8]) -> IncompleteResult<(&[u8], Self)> { 185 | match parser::parse(input) { 186 | Ok((input, packet)) => { 187 | if packet.protocol_version == Protocol::Zlib { 188 | let mut z = ZlibDecoder::new(Cursor::new(packet.data)); 189 | let mut buf = Vec::new(); 190 | if let Err(e) = z.read_to_end(&mut buf) { 191 | return IncompleteResult::Err(ParseError::ZlibError(e)); 192 | } 193 | 194 | match parser::parse(&buf) { 195 | Ok((_, packet)) => IncompleteResult::Ok((input, packet)), 196 | Err(Err::Incomplete(needed)) => { 197 | IncompleteResult::Err(ParseError::PacketError(format!( 198 | "incomplete buffer: {:?} needed", 199 | needed 200 | ))) 201 | } 202 | Err(Err::Error(e) | Err::Failure(e)) => { 203 | IncompleteResult::Err(ParseError::PacketError(format!("{:?}", e))) 204 | } 205 | } 206 | } else { 207 | IncompleteResult::Ok((input, packet)) 208 | } 209 | } 210 | Err(Err::Incomplete(needed)) => IncompleteResult::Incomplete(needed), 211 | Err(Err::Error(e) | Err::Failure(e)) => { 212 | IncompleteResult::Err(ParseError::PacketError(format!("{:?}", e))) 213 | } 214 | } 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /bililive-core/src/packet/parser.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryFrom; 2 | 3 | use nom::bytes::streaming::take; 4 | use nom::combinator::{map, map_res}; 5 | use nom::number::streaming::{be_u16, be_u32}; 6 | use nom::sequence::tuple; 7 | use nom::IResult; 8 | 9 | use super::types::{Operation, Protocol}; 10 | use super::Packet; 11 | 12 | fn parse_proto(input: &[u8]) -> IResult<&[u8], Protocol> { 13 | map_res(be_u16, Protocol::try_from)(input) 14 | } 15 | 16 | fn parse_op(input: &[u8]) -> IResult<&[u8], Operation> { 17 | map(be_u32, Operation::from)(input) 18 | } 19 | 20 | pub fn parse(input: &[u8]) -> IResult<&[u8], Packet> { 21 | let (input, (packet_length, header_length, protocol_version, op, seq_id)) = 22 | tuple((be_u32, be_u16, parse_proto, parse_op, be_u32))(input)?; 23 | let (input, data) = take(packet_length - u32::from(header_length))(input)?; 24 | Ok(( 25 | input, 26 | Packet { 27 | packet_length, 28 | header_length, 29 | protocol_version, 30 | op, 31 | seq_id, 32 | data: data.to_vec(), 33 | }, 34 | )) 35 | } 36 | -------------------------------------------------------------------------------- /bililive-core/src/packet/tests.rs: -------------------------------------------------------------------------------- 1 | use std::fs::read; 2 | 3 | use serde_json::json; 4 | 5 | use crate::errors::IncompleteResult; 6 | 7 | use super::types::{Operation, Protocol}; 8 | use super::Packet; 9 | 10 | fn test_packet(path: &str, expect: Packet, skip_encode: bool) { 11 | let content = read(path).unwrap(); 12 | let res = Packet::parse(&content); 13 | if let IncompleteResult::Ok((_, packet)) = res { 14 | assert_eq!(packet, expect); 15 | if !skip_encode { 16 | assert_eq!(content, expect.encode()); 17 | } 18 | } else { 19 | panic!("error while parsing"); 20 | } 21 | } 22 | 23 | #[test] 24 | fn must_parse_int32be() { 25 | // This package is a bit strange .. 26 | let mut expected = Packet::new( 27 | Operation::HeartBeatResponse, 28 | Protocol::Json, 29 | 358069i32.to_be_bytes(), 30 | ); 31 | expected.set_seq_id(0); 32 | test_packet("tests/raw/int32be.packet", expected, false); 33 | } 34 | 35 | #[test] 36 | fn must_parse_json() { 37 | test_packet( 38 | "tests/raw/json.packet", 39 | Packet::new( 40 | Operation::RoomEnterResponse, 41 | Protocol::Json, 42 | serde_json::to_vec(&json!({ 43 | "code": 0 44 | })) 45 | .unwrap(), 46 | ), 47 | false, 48 | ); 49 | } 50 | 51 | #[test] 52 | fn must_parse_buffer() { 53 | let json_value = json!({ 54 | "cmd": "INTERACT_WORD", 55 | "data": { 56 | "contribution": {"grade": 0}, 57 | "dmscore": 2, 58 | "fans_medal": { 59 | "anchor_roomid": 0, 60 | "guard_level": 0, 61 | "icon_id": 0, 62 | "is_lighted": 0, 63 | "medal_color": 0, 64 | "medal_color_border": 0, 65 | "medal_color_end": 0, 66 | "medal_color_start": 0, 67 | "medal_level": 0, 68 | "medal_name": "", 69 | "score": 0, 70 | "special": "", 71 | "target_id": 0 72 | }, 73 | "identities": [1], 74 | "is_spread": 0, 75 | "msg_type": 1, 76 | "roomid": 23090051, 77 | "score": 1626324624442i64, 78 | "spread_desc": "", 79 | "spread_info": "", 80 | "tail_icon": 0, 81 | "timestamp": 1626324624, 82 | "trigger_time": 1626324623404263200i64, 83 | "uid": 174102117, 84 | "uname": "vioIet・伊芙加登", 85 | "uname_color": "" 86 | }}); 87 | let mut expected = Packet::new( 88 | Operation::Notification, 89 | Protocol::Json, 90 | serde_json::to_vec(&json_value).unwrap(), 91 | ); 92 | expected.set_seq_id(0); 93 | test_packet("tests/raw/buffer.packet", expected, true); 94 | } 95 | -------------------------------------------------------------------------------- /bililive-core/src/packet/types.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryFrom; 2 | 3 | use crate::errors::ParseError; 4 | 5 | /// Live event types. 6 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] 7 | #[repr(u32)] 8 | pub enum Operation { 9 | HeartBeat = 2, 10 | HeartBeatResponse = 3, 11 | Notification = 5, 12 | RoomEnter = 7, 13 | RoomEnterResponse = 8, 14 | Unknown = u32::MAX, 15 | } 16 | 17 | impl From for Operation { 18 | fn from(i: u32) -> Self { 19 | match i { 20 | 2 => Self::HeartBeat, 21 | 3 => Self::HeartBeatResponse, 22 | 5 => Self::Notification, 23 | 7 => Self::RoomEnter, 24 | 8 => Self::RoomEnterResponse, 25 | _ => Self::Unknown, 26 | } 27 | } 28 | } 29 | 30 | /// Protocol types. 31 | /// 32 | /// Indicating the format of packet content. 33 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] 34 | #[repr(u16)] 35 | pub enum Protocol { 36 | Json = 0, 37 | Int32BE = 1, 38 | Zlib = 2, 39 | } 40 | 41 | impl TryFrom for Protocol { 42 | type Error = ParseError; 43 | 44 | fn try_from(value: u16) -> Result { 45 | match value { 46 | 0 => Ok(Self::Json), 47 | 1 => Ok(Self::Int32BE), 48 | 2 => Ok(Self::Zlib), 49 | _ => Err(ParseError::UnknownProtocol), 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /bililive-core/src/retry/config.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Debug, Formatter, Result as FmtResult}; 2 | use std::time::Duration; 3 | 4 | use stream_reconnect::ReconnectOptions; 5 | 6 | use super::policy::BEBIterator; 7 | 8 | /// The configuration for retry behavior. 9 | #[derive(Clone)] 10 | pub struct RetryConfig(ReconnectOptions); 11 | 12 | impl RetryConfig { 13 | /// Create a retry configuration with given `duration_generator`. 14 | /// 15 | /// `duration_generator` is a function that returns a duration iterator. 16 | /// Each item yielded by the iterator indicates the delay time before next connection attempt after a disconnection occurs. 17 | /// If `None` is returned, the stream fails. 18 | /// 19 | /// The `default` implementation uses [`BEBIterator`](BEBIterator). 20 | pub fn new(duration_generator: F) -> Self 21 | where 22 | F: 'static + Send + Sync + Fn() -> IN, 23 | I: 'static + Send + Sync + Iterator, 24 | IN: IntoIterator, 25 | { 26 | Self(ReconnectOptions::new().with_retries_generator(duration_generator)) 27 | } 28 | } 29 | 30 | impl From for ReconnectOptions { 31 | fn from(o: RetryConfig) -> Self { 32 | o.0 33 | } 34 | } 35 | 36 | impl Debug for RetryConfig { 37 | fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { 38 | f.debug_tuple("RetryConfig") 39 | .field(&"") 40 | .finish() 41 | } 42 | } 43 | 44 | impl Default for RetryConfig { 45 | fn default() -> Self { 46 | Self::new(BEBIterator::default) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /bililive-core/src/retry/context.rs: -------------------------------------------------------------------------------- 1 | use std::sync::atomic::AtomicUsize; 2 | use std::sync::atomic::Ordering::SeqCst; 3 | use std::sync::Arc; 4 | 5 | use crate::config::StreamConfig; 6 | 7 | /// Internal context for server picking during (re)connection. 8 | /// 9 | /// Implements a round-robin policy for server selection. 10 | #[derive(Debug, Clone)] 11 | pub struct RetryContext { 12 | config: StreamConfig, 13 | cursor: Arc, 14 | } 15 | 16 | impl RetryContext { 17 | /// Get the stream config. 18 | #[must_use] 19 | pub const fn config(&self) -> &StreamConfig { 20 | &self.config 21 | } 22 | /// Get the next server. 23 | #[allow(clippy::missing_panics_doc)] 24 | pub fn get(&mut self) -> &str { 25 | let cursor: usize = self 26 | .cursor 27 | .fetch_update(SeqCst, SeqCst, |i| { 28 | Some((i + 1) % self.config.servers().len()) 29 | }) 30 | .unwrap(); 31 | &*self.config.servers()[cursor] 32 | } 33 | } 34 | 35 | impl From for RetryContext { 36 | fn from(config: StreamConfig) -> Self { 37 | Self { 38 | config, 39 | cursor: Arc::new(Default::default()), 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /bililive-core/src/retry/mod.rs: -------------------------------------------------------------------------------- 1 | //! Traits and types used by retry mechanism. 2 | 3 | use std::future::Future; 4 | use std::io; 5 | use std::io::ErrorKind; 6 | use std::marker::PhantomData; 7 | use std::pin::Pin; 8 | 9 | use futures::SinkExt; 10 | use futures::{Sink, Stream}; 11 | use stream_reconnect::UnderlyingStream; 12 | 13 | pub use config::RetryConfig; 14 | pub use context::RetryContext; 15 | pub use policy::BEBIterator; 16 | 17 | use crate::errors::StreamError; 18 | use crate::packet::Packet; 19 | 20 | mod config; 21 | mod context; 22 | mod policy; 23 | 24 | /// Trait of helper objects to connect bilibili websocket server. 25 | /// 26 | /// This trait is used when constructing normal bililive streams or auto-retry bililive streams. 27 | /// 28 | /// An implementation of `WsStreamTrait` takes in a ws server url and decodes the data into a stream 29 | /// of [`Packet`](crate::packet::Packet) with heartbeat auto-response mechanism implemented 30 | /// (see [`HeartbeatStream`](crate::stream::HeartbeatStream) for details). 31 | #[cfg(feature = "not-send")] 32 | pub trait WsStreamTrait { 33 | /// The returned stream type. 34 | type Stream: Stream>> 35 | + Sink> 36 | + Unpin 37 | + Sized; 38 | /// Connect to bilibili websocket server. 39 | /// 40 | /// # Errors 41 | /// Returns an error when websocket connection fails. 42 | fn connect(url: &str) -> Pin> + '_>>; 43 | } 44 | 45 | #[cfg(not(feature = "not-send"))] 46 | pub trait WsStreamTrait { 47 | /// The returned stream type. 48 | type Stream: Stream>> 49 | + Sink> 50 | + Unpin 51 | + Sized 52 | + Send; 53 | /// Connect to bilibili websocket server. 54 | /// 55 | /// # Errors 56 | /// Returns an error when websocket connection fails. 57 | fn connect(url: &str) -> Pin> + Send + '_>>; 58 | } 59 | 60 | /// Wrapper for types implementing `WsStreamTrait`. 61 | /// 62 | /// This type is used to avoid the orphan rule. Exposed for stream type construction. 63 | #[derive(Debug, Default)] 64 | pub struct WsStream, E>(PhantomData<(T, E)>); 65 | 66 | impl WsStream 67 | where 68 | T: WsStreamTrait, 69 | { 70 | /// Connect to bilibili websocket server. 71 | /// 72 | /// # Errors 73 | /// Returns an error when websocket connection fails. 74 | pub async fn connect(url: &str) -> Result { 75 | T::connect(url).await 76 | } 77 | } 78 | 79 | #[allow(clippy::type_complexity)] 80 | impl UnderlyingStream>, StreamError> 81 | for WsStream 82 | where 83 | T: WsStreamTrait, 84 | E: std::error::Error, 85 | { 86 | type Stream = T::Stream; 87 | 88 | #[cfg(feature = "not-send")] 89 | fn establish( 90 | mut ctor_arg: RetryContext, 91 | ) -> Pin>>>> { 92 | Box::pin(async move { 93 | let server = ctor_arg.get(); 94 | let mut ws = Self::connect(server) 95 | .await 96 | .map_err(StreamError::from_ws_error)?; 97 | ws.send(Packet::new_room_enter(ctor_arg.config())).await?; 98 | Ok(ws) 99 | }) 100 | } 101 | 102 | #[cfg(not(feature = "not-send"))] 103 | fn establish( 104 | mut ctor_arg: RetryContext, 105 | ) -> Pin>> + Send>> { 106 | Box::pin(async move { 107 | let server = ctor_arg.get(); 108 | let mut ws = Self::connect(server) 109 | .await 110 | .map_err(StreamError::from_ws_error)?; 111 | ws.send(Packet::new_room_enter(ctor_arg.config())).await?; 112 | Ok(ws) 113 | }) 114 | } 115 | 116 | fn is_write_disconnect_error(err: &StreamError) -> bool { 117 | matches!(err, StreamError::WebSocket(_) | StreamError::IO(_)) 118 | } 119 | 120 | fn is_read_disconnect_error(item: &Result>) -> bool { 121 | if let Err(e) = item { 122 | Self::is_write_disconnect_error(e) 123 | } else { 124 | false 125 | } 126 | } 127 | 128 | fn exhaust_err() -> StreamError { 129 | StreamError::IO(io::Error::new( 130 | ErrorKind::NotConnected, 131 | "Disconnected. Connection attempts have been exhausted.", 132 | )) 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /bililive-core/src/retry/policy.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use rand::distributions::Uniform; 4 | use rand::{thread_rng, Rng}; 5 | 6 | /// An exponential backoff retry policy. 7 | #[derive(Debug, Clone)] 8 | pub struct BEBIterator { 9 | unit: Duration, 10 | truncate: u32, 11 | fail: u32, 12 | count: u32, 13 | } 14 | 15 | impl Default for BEBIterator { 16 | fn default() -> Self { 17 | Self::new(Duration::from_secs(1), 5, 10) 18 | } 19 | } 20 | 21 | impl BEBIterator { 22 | /// Create an exponential backoff retry policy 23 | /// 24 | /// # Arguments 25 | /// 26 | /// * `unit`: unit duration of delay. 27 | /// * `truncate`: after a continuous failure of such counts, the delay stops increasing. 28 | /// * `fail`: after a continuous failure of such counts, the connection closes. 29 | /// 30 | /// returns: `BEBIterator` 31 | /// 32 | /// # Panics 33 | /// 34 | /// Truncate is expected to less than fail. Otherwise, a panic will occur. 35 | #[must_use] 36 | pub fn new(unit: Duration, truncate: u32, fail: u32) -> Self { 37 | assert!(truncate < fail, "truncate >= fail"); 38 | Self { 39 | unit, 40 | truncate, 41 | fail, 42 | count: 0, 43 | } 44 | } 45 | } 46 | 47 | impl Iterator for BEBIterator { 48 | type Item = Duration; 49 | 50 | fn next(&mut self) -> Option { 51 | if self.count >= self.fail { 52 | None 53 | } else { 54 | let max_delay = 2_u32.pow(if self.count >= self.truncate { 55 | self.truncate 56 | } else { 57 | self.count 58 | }); 59 | let between = Uniform::new_inclusive(0, max_delay * 100); 60 | let units = thread_rng().sample(between); 61 | Some(self.unit * units / 100) 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /bililive-core/src/stream/heartbeat.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::pin::Pin; 3 | use std::sync::Arc; 4 | use std::task::Waker; 5 | use std::task::{Context, Poll}; 6 | use std::time::{Duration, Instant}; 7 | 8 | use futures::ready; 9 | use futures::{Sink, Stream}; 10 | use log::debug; 11 | 12 | use crate::errors::StreamError; 13 | use crate::packet::{Operation, Packet, Protocol}; 14 | 15 | use super::waker::WakerProxy; 16 | 17 | /// Wrapper that implement heartbeat auto-response mechanism on a [`Packet`](crate::packet::Packet) stream. 18 | /// 19 | /// Bilibili server requires that every client must respond to a ping packet in 60 seconds. If no 20 | /// response is sent, the connection will be closed remotely. 21 | /// 22 | /// `HeartbeatStream` ensures that a pong packet is sent every 30 seconds. 23 | pub struct HeartbeatStream { 24 | /// underlying bilibili stream 25 | stream: T, 26 | /// waker proxy for tx, see WakerProxy for details 27 | tx_waker: Arc, 28 | /// last time when heart beat is sent 29 | last_hb: Option, 30 | __marker: PhantomData, 31 | } 32 | 33 | impl Unpin for HeartbeatStream {} 34 | 35 | impl HeartbeatStream { 36 | /// Add heartbeat response mechanism to the underlying bililive stream. 37 | pub fn new(stream: T) -> Self { 38 | Self { 39 | stream, 40 | tx_waker: Arc::new(Default::default()), 41 | last_hb: None, 42 | __marker: PhantomData, 43 | } 44 | } 45 | 46 | fn with_context(&mut self, f: F) -> U 47 | where 48 | F: FnOnce(&mut Context<'_>, &mut T) -> U, 49 | { 50 | let waker = Waker::from(self.tx_waker.clone()); 51 | let mut cx = Context::from_waker(&waker); 52 | 53 | f(&mut cx, &mut self.stream) 54 | } 55 | } 56 | 57 | impl Stream for HeartbeatStream 58 | where 59 | T: Stream>> + Sink> + Unpin, 60 | E: std::error::Error, 61 | { 62 | type Item = Result>; 63 | 64 | fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 65 | // register current task to be waken on poll_ready 66 | self.tx_waker.rx(cx.waker()); 67 | 68 | // ensure that all pending write op are completed 69 | ready!(self.with_context(|cx, s| Pin::new(s).poll_ready(cx)))?; 70 | 71 | // check whether we need to send heartbeat now. 72 | let now = Instant::now(); 73 | let need_hb = self 74 | .last_hb 75 | .map_or(true, |last_hb| now - last_hb >= Duration::from_secs(30)); 76 | 77 | if need_hb { 78 | // we need to send heartbeat, so push it into the sink 79 | debug!("sending heartbeat"); 80 | self.as_mut() 81 | .start_send(Packet::new(Operation::HeartBeat, Protocol::Json, vec![]))?; 82 | 83 | // Update the time we sent the heartbeat. 84 | // It must be earlier than other non-blocking op so that heartbeat 85 | // won't be sent repeatedly. 86 | self.last_hb = Some(now); 87 | 88 | // Schedule current task to be waken in case there's no incoming 89 | // websocket message in a long time. 90 | #[cfg(feature = "tokio")] 91 | { 92 | let waker = cx.waker().clone(); 93 | tokio1::spawn(async { 94 | tokio1::time::sleep(Duration::from_secs(30)).await; 95 | waker.wake(); 96 | }); 97 | } 98 | #[cfg(feature = "async-std")] 99 | { 100 | let waker = cx.waker().clone(); 101 | async_std1::task::spawn(async { 102 | async_std1::task::sleep(Duration::from_secs(30)).await; 103 | waker.wake(); 104 | }); 105 | } 106 | 107 | // ensure that heartbeat is sent 108 | ready!(self.with_context(|cx, s| Pin::new(s).poll_flush(cx)))?; 109 | } 110 | 111 | Pin::new(&mut self.stream).poll_next(cx) 112 | } 113 | } 114 | 115 | impl Sink for HeartbeatStream 116 | where 117 | T: Sink> + Unpin, 118 | E: std::error::Error, 119 | { 120 | type Error = StreamError; 121 | 122 | fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 123 | // wake current task and stream task 124 | self.tx_waker.tx(cx.waker()); 125 | 126 | // poll the underlying websocket sink 127 | self.with_context(|cx, s| Pin::new(s).poll_ready(cx)) 128 | } 129 | 130 | fn start_send(mut self: Pin<&mut Self>, item: Packet) -> Result<(), Self::Error> { 131 | Pin::new(&mut self.stream).start_send(item) 132 | } 133 | 134 | fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 135 | // wake current task and stream task 136 | self.tx_waker.tx(cx.waker()); 137 | 138 | // poll the underlying websocket sink 139 | self.with_context(|cx, s| Pin::new(s).poll_flush(cx)) 140 | } 141 | 142 | fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 143 | // wake current task and stream task 144 | self.tx_waker.tx(cx.waker()); 145 | 146 | // poll the underlying websocket sink 147 | self.with_context(|cx, s| Pin::new(s).poll_close(cx)) 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /bililive-core/src/stream/mod.rs: -------------------------------------------------------------------------------- 1 | //! Stream types. 2 | 3 | pub use heartbeat::HeartbeatStream; 4 | 5 | mod heartbeat; 6 | pub mod waker; 7 | -------------------------------------------------------------------------------- /bililive-core/src/stream/waker.rs: -------------------------------------------------------------------------------- 1 | //! Helper waker proxy type. 2 | 3 | use std::sync::Arc; 4 | use std::task::{Wake, Waker}; 5 | 6 | use futures::task::AtomicWaker; 7 | 8 | /// When reading the stream, a `poll_ready` is executed to ensure that all pending write op including 9 | /// heartbeat is completed. 10 | /// Therefore, we need to wake the task on which stream is polled in `poll_ready` (and `poll_flush`). 11 | /// `WakerProxy` is a waker dispatcher. It will dispatch a wake op to both wakers (rx & tx), such that 12 | /// both stream task and sink task can be waken and no starvation will occur. 13 | #[derive(Debug, Default)] 14 | pub struct WakerProxy { 15 | tx_waker: AtomicWaker, 16 | rx_waker: AtomicWaker, 17 | } 18 | 19 | impl WakerProxy { 20 | /// Register the read waker. 21 | pub fn rx(&self, waker: &Waker) { 22 | self.rx_waker.register(waker); 23 | } 24 | /// Register the write waker. 25 | pub fn tx(&self, waker: &Waker) { 26 | self.tx_waker.register(waker); 27 | } 28 | } 29 | 30 | impl Wake for WakerProxy { 31 | fn wake(self: Arc) { 32 | self.rx_waker.wake(); 33 | self.tx_waker.wake(); 34 | } 35 | 36 | fn wake_by_ref(self: &Arc) { 37 | self.rx_waker.wake(); 38 | self.tx_waker.wake(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /bililive-core/tests/getConf.json: -------------------------------------------------------------------------------- 1 | {"code":0,"msg":"ok","message":"ok","data":{"refresh_row_factor":0.125,"refresh_rate":100,"max_delay":5000,"port":2243,"host":"broadcastlv.chat.bilibili.com","host_server_list":[{"host":"tx-gz-live-comet-03.chat.bilibili.com","port":2243,"wss_port":443,"ws_port":2244},{"host":"tx-sh-live-comet-03.chat.bilibili.com","port":2243,"wss_port":443,"ws_port":2244},{"host":"broadcastlv.chat.bilibili.com","port":2243,"wss_port":443,"ws_port":2244}],"server_list":[{"host":"106.53.116.19","port":2243},{"host":"49.235.252.230","port":2243},{"host":"broadcastlv.chat.bilibili.com","port":2243},{"host":"106.53.116.19","port":80},{"host":"49.235.252.230","port":80},{"host":"broadcastlv.chat.bilibili.com","port":80}],"token":"zRLe_Wb0lwdalke2_OMvIxBD7uBQ7pNKepn-fP2rIV91AyCRSAYwsw1CVYGgjtuf8IA1AHLchDXhiekQ3IMWnzBu5zqIK9CqdY-tuaCpVi1fxE_hqBEdsfdgxPJyFQAxtgqK4cdf1dm7"}} -------------------------------------------------------------------------------- /bililive-core/tests/raw/buffer.packet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LightQuantumArchive/bililive-rs/195d782a84792838a412fc0deeb6acd37a52977e/bililive-core/tests/raw/buffer.packet -------------------------------------------------------------------------------- /bililive-core/tests/raw/int32be.packet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LightQuantumArchive/bililive-rs/195d782a84792838a412fc0deeb6acd37a52977e/bililive-core/tests/raw/int32be.packet -------------------------------------------------------------------------------- /bililive-core/tests/raw/json.packet: -------------------------------------------------------------------------------- 1 | {"code":0} -------------------------------------------------------------------------------- /bililive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bililive" 3 | version = "0.2.0-beta.5" 4 | authors = ["LightQuantum "] 5 | edition = "2021" 6 | description = "A simple stream-based bilibili live client library." 7 | license = "MIT" 8 | keywords = ["bilibili", "live", "stream", "client", "danmaku"] 9 | repository = "https://github.com/PhotonQuantum/bililive-rs" 10 | readme = "README.md" 11 | 12 | [package.metadata.docs.rs] 13 | all-features = true 14 | 15 | [features] 16 | default = ["tokio-native-tls"] 17 | tokio-native-tls = ["tokio", "async-tungstenite/tokio-native-tls", "reqwest/native-tls", "stream-reconnect/tokio", "bililive-core/tokio"] 18 | tokio-rustls-webpki-roots = ["tokio", "async-tungstenite/tokio-rustls-webpki-roots", "reqwest/rustls-tls-webpki-roots", "stream-reconnect/tokio", "bililive-core/tokio"] 19 | tokio-rustls-native-certs = ["tokio", "async-tungstenite/tokio-rustls-native-certs", "reqwest/rustls-tls-native-roots", "stream-reconnect/tokio", "bililive-core/tokio"] 20 | async-native-tls = ["async-std", "async-tungstenite/async-native-tls", "h1-client", "http-client/native-tls", "stream-reconnect/async-std", "bililive-core/async-std"] 21 | h1-client = ["http-client/h1_client"] 22 | 23 | [dependencies] 24 | async-std = { version = "1.12", optional = true } 25 | async-tungstenite = { version = "0.23", default-features = false } 26 | bililive-core = { version = "0.1.0-beta.4", path = "../bililive-core", default-features = false } 27 | futures = "0.3" 28 | http-client = { version = "6.5", default-features = false, optional = true } 29 | log = "0.4" 30 | reqwest = { version = "0.11", default-features = false, optional = true } 31 | serde = "1.0" 32 | serde_json = "1.0" 33 | stream-reconnect = { version = "0.4.0-beta.4", default-features = false } 34 | thiserror = "1.0" 35 | tokio = { version = "1.36", optional = true } 36 | url = { version = "2.5", features = ["serde"] } 37 | 38 | [dev-dependencies] 39 | async-std = { version = "1.12", features = ["attributes"] } 40 | pretty_env_logger = "0.5" 41 | tokio = { version = "1.36", features = ["macros", "rt-multi-thread"] } 42 | tokio-test = "0.4" -------------------------------------------------------------------------------- /bililive/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 lightquantum 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /bililive/README.md: -------------------------------------------------------------------------------- 1 | # bililive-rs 2 | 3 | [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/PhotonQuantum/bililive-rs/Test?style=flat-square)](https://github.com/PhotonQuantum/bililive-rs/actions/workflows/test.yml) 4 | [![crates.io](https://img.shields.io/crates/v/bililive?style=flat-square)](https://crates.io/crates/bililive) 5 | [![Documentation](https://img.shields.io/docsrs/bililive?style=flat-square)](https://docs.rs/bililive) 6 | 7 | A simple stream-based bilibili live client library backed by [async-tungstenite](https://github.com/sdroege/async-tungstenite). 8 | 9 | To use with your project, add the following to your Cargo.toml: 10 | 11 | ``` 12 | bililive = "0.2.0-beta.1" 13 | ``` 14 | 15 | *Minimum supported rust version: 1.56.0* 16 | 17 | ## Runtime Support 18 | 19 | This crate supports both `tokio` and `async-std` runtime. 20 | 21 | `tokio` support is enabled by default. While used on an `async-std` runtime, change the corresponding dependency in 22 | Cargo.toml to 23 | 24 | ``` 25 | bililive = { version = "0.2.0-beta.1", default-features = false, features = ["async-native-tls"] } 26 | ``` 27 | 28 | See `Crates Features` section for more. 29 | 30 | ## Features 31 | 32 | - Ergonomic `Stream`/`Sink` interface. 33 | - Easy establishment of connection via given live room id. 34 | - Handles heartbeat packets automatically. 35 | - Auto retry when connection fails (optional). 36 | - Decompresses `Zlib` payloads automatically. 37 | 38 | ## Example 39 | 40 | ```rust 41 | use bililive::connect::tokio::connect_with_retry; 42 | use bililive::{ConfigBuilder, RetryConfig}; 43 | 44 | use futures::StreamExt; 45 | use log::info; 46 | use serde_json::Value; 47 | 48 | let config = ConfigBuilder::new() 49 | .by_uid(1602085) 50 | .await 51 | .unwrap() 52 | .fetch_conf() 53 | .await 54 | .unwrap() 55 | .build(); 56 | 57 | let mut stream = connect_with_retry(config, RetryConfig::default()).await.unwrap(); 58 | while let Some(e) = stream.next().await { 59 | match e { 60 | Ok(packet) => { 61 | info!("raw: {:?}", packet); 62 | if let Ok(json) = packet.json::() { 63 | info!("json: {:?}", json); 64 | } 65 | } 66 | Err(e) => { 67 | info!("err: {:?}", e); 68 | } 69 | } 70 | } 71 | ``` 72 | 73 | ## Crate Features 74 | 75 | - `tokio-native-tls`(default): Enables `tokio` support with TLS implemented 76 | via [tokio-native-tls](https://crates.io/crates/tokio-native-tls). 77 | - `tokio-rustls-native-certs`: Enables `tokio` support with TLS implemented 78 | via [tokio-rustls](https://crates.io/crates/tokio-rustls) and uses native system certificates found 79 | with [rustls-native-certs](https://github.com/rustls/rustls-native-certs). 80 | - `tokio-rustls-webpki-roots`: Enables `tokio` support with TLS implemented 81 | via [tokio-rustls](https://crates.io/crates/tokio-rustls) and uses the 82 | certificates [webpki-roots](https://github.com/rustls/webpki-roots) provides. 83 | - `async-native-tls`: Enables `async_std` support with TLS implemented 84 | via [async-native-tls](https://crates.io/crates/async-native-tls). -------------------------------------------------------------------------------- /bililive/examples/simple.rs: -------------------------------------------------------------------------------- 1 | use futures::StreamExt; 2 | use log::info; 3 | use serde_json::Value; 4 | 5 | use bililive::{ConfigBuilder, RetryConfig}; 6 | 7 | async fn run() { 8 | pretty_env_logger::init(); 9 | 10 | let config = ConfigBuilder::new() 11 | .by_uid(1602085) 12 | .await 13 | .unwrap() 14 | .fetch_conf() 15 | .await 16 | .unwrap() 17 | .build(); 18 | info!("room_id: {}", config.room_id()); 19 | info!("uid: {}", config.uid()); 20 | info!("token: {}", config.token()); 21 | info!("servers: {:#?}", config.servers()); 22 | 23 | #[cfg(feature = "tokio")] 24 | let mut stream = 25 | bililive::connect::tokio::connect_with_retry(config.clone(), RetryConfig::default()) 26 | .await 27 | .unwrap(); 28 | #[cfg(feature = "async-std")] 29 | let mut stream = 30 | bililive::connect::async_std::connect_with_retry(config, RetryConfig::default()) 31 | .await 32 | .unwrap(); 33 | 34 | while let Some(e) = stream.next().await { 35 | match e { 36 | Ok(packet) => { 37 | info!("raw: {:?}", packet); 38 | if let Ok(json) = packet.json::() { 39 | info!("json: {:?}", json); 40 | } 41 | } 42 | Err(e) => { 43 | info!("err: {:?}", e); 44 | } 45 | } 46 | } 47 | } 48 | 49 | fn main() { 50 | #[cfg(feature = "tokio")] 51 | { 52 | let runtime = tokio::runtime::Builder::new_multi_thread() 53 | .enable_all() 54 | .build() 55 | .unwrap(); 56 | return runtime.block_on(run()); 57 | } 58 | #[cfg(feature = "async-std")] 59 | return async_std::task::block_on(run()); 60 | } 61 | -------------------------------------------------------------------------------- /bililive/src/builder/h1.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::pin::Pin; 3 | 4 | use http_client::h1::H1Client as Client; 5 | use http_client::HttpClient; 6 | use serde::de::DeserializeOwned; 7 | 8 | use crate::core::builder::Requester; 9 | 10 | use super::BoxedError; 11 | 12 | #[derive(Debug, Default)] 13 | pub struct H1Client(Client); 14 | 15 | impl From for H1Client { 16 | fn from(client: Client) -> Self { 17 | Self(client) 18 | } 19 | } 20 | 21 | impl Requester for H1Client { 22 | fn get_json( 23 | &self, 24 | url: &str, 25 | ) -> Pin> + Send + '_>> { 26 | let req = http_client::Request::get(url); 27 | Box::pin(async move { 28 | Ok(serde_json::from_slice( 29 | &*self.0.send(req).await?.body_bytes().await?, 30 | )?) 31 | }) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /bililive/src/builder/mod.rs: -------------------------------------------------------------------------------- 1 | //! `bililive` config builder. 2 | //! 3 | //! Stream config can be built via given live room parameters (room id and user id) & danmaku server configs (server token and list). 4 | //! 5 | //! # Helper methods 6 | //! 7 | //! [`by_uid`](ConfigBuilder::by_uid) fetches room id by given user id. 8 | //! 9 | //! [`fetch_conf`](ConfigBuilder::fetch_conf) fetches danmaku server token and list without any input parameter. 10 | //! 11 | //! # Example 12 | //! 13 | //! ```rust 14 | //! # use std::future::Future; 15 | //! # 16 | //! # use bililive::ConfigBuilder; 17 | //! # use bililive::core::errors::BuildError; 18 | //! # 19 | //! # let fut = async { 20 | //! # Ok::<_, BuildError>( 21 | //! ConfigBuilder::new() 22 | //! .by_uid(1472906636) 23 | //! .await? 24 | //! .fetch_conf() 25 | //! .await? 26 | //! .build() 27 | //! # ) 28 | //! # }; 29 | //! # 30 | //! # tokio_test::block_on(fut).unwrap(); 31 | //! ``` 32 | 33 | #[cfg(feature = "h1-client")] 34 | mod h1; 35 | #[cfg(feature = "reqwest")] 36 | mod reqwest; 37 | #[cfg(test)] 38 | pub(crate) mod tests; 39 | 40 | type BoxedError = Box; 41 | 42 | /// `bililive` stream config builder. 43 | /// 44 | /// Stream config can be built via given live room parameters (room id and user id) & danmaku server configs (server token and list). 45 | /// 46 | /// See the generic type [`ConfigBuilder`](bililive_core::builder::ConfigBuilder) for details. 47 | /// 48 | /// # Helper methods 49 | /// 50 | /// [`by_uid`](ConfigBuilder::by_uid) fetches room id by given user id. 51 | /// 52 | /// [`fetch_conf`](ConfigBuilder::fetch_conf) fetches danmaku server token and list without any input parameter. 53 | #[cfg(feature = "reqwest")] 54 | pub type ConfigBuilder = 55 | bililive_core::builder::ConfigBuilder; 56 | 57 | /// `bililive` stream config builder. 58 | /// 59 | /// Stream config can be built via given live room parameters (room id and user id) & danmaku server configs (server token and list). 60 | /// 61 | /// See the generic type [`ConfigBuilder`](bililive_core::builder::ConfigBuilder) for details. 62 | /// 63 | /// # Helper methods 64 | /// 65 | /// [`by_uid`](ConfigBuilder::by_uid) fetches room id by given user id. 66 | /// 67 | /// [`fetch_conf`](ConfigBuilder::fetch_conf) fetches danmaku server token and list without any input parameter. 68 | #[cfg(feature = "h1-client")] 69 | #[cfg(not(feature = "reqwest"))] 70 | pub type ConfigBuilder = 71 | bililive_core::builder::ConfigBuilder; 72 | -------------------------------------------------------------------------------- /bililive/src/builder/reqwest.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::pin::Pin; 3 | use std::str::FromStr; 4 | 5 | use reqwest::Client; 6 | use serde::de::DeserializeOwned; 7 | use url::Url; 8 | 9 | use crate::core::builder::Requester; 10 | 11 | use super::BoxedError; 12 | 13 | #[derive(Debug, Default)] 14 | pub struct ReqwestClient(Client); 15 | 16 | impl From for ReqwestClient { 17 | fn from(client: Client) -> Self { 18 | Self(client) 19 | } 20 | } 21 | 22 | impl Requester for ReqwestClient { 23 | fn get_json( 24 | &self, 25 | url: &str, 26 | ) -> Pin> + Send + '_>> { 27 | let url = Url::from_str(url).unwrap(); 28 | Box::pin(async move { 29 | Ok(serde_json::from_slice( 30 | &*self.0.get(url).send().await?.bytes().await?, 31 | )?) 32 | }) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /bililive/src/builder/tests.rs: -------------------------------------------------------------------------------- 1 | use bililive_core::config::StreamConfig; 2 | 3 | use super::ConfigBuilder; 4 | 5 | pub(crate) async fn build_real_config(override_servers: bool) -> StreamConfig { 6 | let builder = ConfigBuilder::new() 7 | .by_uid(419220) 8 | .await 9 | .expect("unable to fetch room_id") 10 | .fetch_conf() 11 | .await 12 | .expect("unable to fetch server conf"); 13 | let builder = if override_servers { 14 | builder.servers(&["wss://broadcastlv.chat.bilibili.com/sub".to_string()]) 15 | } else { 16 | builder 17 | }; 18 | builder.build() 19 | } 20 | 21 | #[cfg(feature = "tokio")] 22 | #[tokio::test] 23 | async fn must_build_real_config_tokio() { 24 | build_real_config(false).await; 25 | } 26 | 27 | #[cfg(feature = "async-std")] 28 | #[async_std::test] 29 | async fn must_build_real_config_async_std() { 30 | build_real_config(false).await; 31 | } 32 | -------------------------------------------------------------------------------- /bililive/src/connect.rs: -------------------------------------------------------------------------------- 1 | //! Connection related functions and types. 2 | macro_rules! impl_connect_mod { 3 | ($adapter:ident) => { 4 | use std::future::Future; 5 | use std::pin::Pin; 6 | use std::str::FromStr; 7 | 8 | use async_tungstenite::tungstenite::error::Error as WsError; 9 | use async_tungstenite::$adapter::{connect_async, ConnectStream}; 10 | use async_tungstenite::WebSocketStream; 11 | use stream_reconnect::{ReconnectStream, UnderlyingStream}; 12 | use url::Url; 13 | 14 | use crate::core::config::StreamConfig; 15 | use crate::core::errors::StreamError; 16 | use crate::core::packet::Packet; 17 | use crate::core::retry::{RetryConfig, RetryContext, WsStream, WsStreamTrait}; 18 | use crate::core::stream::HeartbeatStream; 19 | use crate::stream::CodecStream; 20 | 21 | /// Raw websocket stream type. 22 | pub type InnerStream = WebSocketStream; 23 | /// Bililive stream type. 24 | pub type DefaultStream = HeartbeatStream, WsError>; 25 | /// Bililive stream type with auto-reconnect mechanism. 26 | pub type RetryStream = ReconnectStream< 27 | WsStream, 28 | RetryContext, 29 | Result>, 30 | StreamError, 31 | >; 32 | 33 | #[doc(hidden)] 34 | pub struct Connector; 35 | 36 | impl WsStreamTrait for Connector { 37 | type Stream = DefaultStream; 38 | fn connect( 39 | url: &str, 40 | ) -> Pin> + Send + '_>> { 41 | let url = Url::from_str(url).unwrap(); 42 | Box::pin(async move { 43 | Ok(HeartbeatStream::new(CodecStream::new( 44 | connect_async(url).await?.0, 45 | ))) 46 | }) 47 | } 48 | } 49 | 50 | /// Connect to bilibili live room. 51 | /// 52 | /// # Errors 53 | /// Returns an error when websocket connection fails. 54 | pub async fn connect(config: StreamConfig) -> Result> { 55 | WsStream::::establish(config.into()).await 56 | } 57 | 58 | /// Connect to bilibili live room with auto retry. 59 | /// 60 | /// # Errors 61 | /// Returns an error when websocket connection fails. 62 | pub async fn connect_with_retry( 63 | stream_config: StreamConfig, 64 | retry_config: RetryConfig, 65 | ) -> Result> { 66 | let inner: RetryStream = 67 | ReconnectStream::connect_with_options(stream_config.into(), retry_config.into()) 68 | .await?; 69 | Ok(inner) 70 | } 71 | }; 72 | } 73 | 74 | #[cfg(feature = "tokio")] 75 | pub mod tokio { 76 | //! `tokio` integration. 77 | impl_connect_mod!(tokio); 78 | } 79 | 80 | #[cfg(feature = "async-std")] 81 | pub mod async_std { 82 | //! `async_std` integration. 83 | impl_connect_mod!(async_std); 84 | } 85 | -------------------------------------------------------------------------------- /bililive/src/errors.rs: -------------------------------------------------------------------------------- 1 | //! Error types. 2 | use async_tungstenite::tungstenite::Error as WsError; 3 | 4 | pub use crate::core::errors::{BuildError, IncompleteResult, ParseError}; 5 | 6 | /// Errors that may occur when consuming a stream. 7 | pub type StreamError = crate::core::errors::StreamError; 8 | -------------------------------------------------------------------------------- /bililive/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A simple stream-based bilibili live client library backed by [async-tungstenite](https://github.com/sdroege/async-tungstenite). 2 | //! 3 | //! *Minimum supported rust version: 1.56.0* 4 | //! 5 | //! ## Runtime Support 6 | //! 7 | //! This crate supports both `tokio` and `async-std` runtime. 8 | //! 9 | //! `tokio` support is enabled by default. While used on an `async-std` runtime, change the corresponding dependency in Cargo.toml to 10 | //! 11 | //! ```toml 12 | //! bililive = { version = "0.2.0-beta.1", default-features = false, features = ["async-native-tls"] } 13 | //! ``` 14 | //! 15 | //! See `Crates Features` section for more. 16 | //! 17 | //! ## Features 18 | //! 19 | //! - Ergonomic `Stream`/`Sink` interface. 20 | //! - Easy establishment of connection via given live room id. 21 | //! - Handles heartbeat packets automatically. 22 | //! - Auto retry when connection fails (optional). 23 | //! - Decompresses `Zlib` payloads automatically. 24 | //! 25 | //! ## Example 26 | //! 27 | //! ```rust 28 | //! # #[cfg(feature = "tokio")] 29 | //! use bililive::connect::tokio::connect_with_retry; 30 | //! use bililive::{ConfigBuilder, RetryConfig}; 31 | //! 32 | //! use futures::StreamExt; 33 | //! use log::info; 34 | //! use serde_json::Value; 35 | //! 36 | //! # #[cfg(feature = "tokio")] 37 | //! # async fn test() { 38 | //! let config = ConfigBuilder::new() 39 | //! .by_uid(1602085) 40 | //! .await 41 | //! .unwrap() 42 | //! .fetch_conf() 43 | //! .await 44 | //! .unwrap() 45 | //! .build(); 46 | //! 47 | //! let mut stream = connect_with_retry(config, RetryConfig::default()).await.unwrap(); 48 | //! while let Some(e) = stream.next().await { 49 | //! match e { 50 | //! Ok(packet) => { 51 | //! info!("raw: {:?}", packet); 52 | //! if let Ok(json) = packet.json::() { 53 | //! info!("json: {:?}", json); 54 | //! } 55 | //! } 56 | //! Err(e) => { 57 | //! info!("err: {:?}", e); 58 | //! } 59 | //! } 60 | //! } 61 | //! # 62 | //! # } 63 | //! ``` 64 | //! 65 | //! ## Crate Features 66 | //! 67 | //! * `tokio-native-tls`(default): Enables `tokio` support with TLS implemented 68 | //! via [tokio-native-tls](https://crates.io/crates/tokio-native-tls). 69 | //! * `tokio-rustls-native-certs`: Enables `tokio` support with TLS implemented 70 | //! via [tokio-rustls](https://crates.io/crates/tokio-rustls) and uses native system certificates found 71 | //! with [rustls-native-certs](https://github.com/rustls/rustls-native-certs). 72 | //! * `tokio-rustls-webpki-roots`: Enables `tokio` support with TLS implemented 73 | //! via [tokio-rustls](https://crates.io/crates/tokio-rustls) and uses the 74 | //! certificates [webpki-roots](https://github.com/rustls/webpki-roots) provides. 75 | //! * `async-native-tls`: Enables `async_std` support with TLS implemented 76 | //! via [async-native-tls](https://crates.io/crates/async-native-tls). 77 | 78 | #![allow(clippy::default_trait_access, clippy::module_name_repetitions)] 79 | 80 | pub use bililive_core as core; 81 | 82 | #[doc(inline)] 83 | pub use crate::builder::ConfigBuilder; 84 | pub use crate::core::packet::*; 85 | pub use crate::core::retry::RetryConfig; 86 | 87 | mod builder; 88 | pub mod connect; 89 | pub mod errors; 90 | pub mod stream; 91 | -------------------------------------------------------------------------------- /bililive/src/stream/codec.rs: -------------------------------------------------------------------------------- 1 | use std::pin::Pin; 2 | use std::task::{Context, Poll}; 3 | 4 | use async_tungstenite::tungstenite::Error as WsError; 5 | use async_tungstenite::tungstenite::Message; 6 | use futures::ready; 7 | use futures::{Sink, Stream}; 8 | use log::{debug, warn}; 9 | 10 | use crate::core::errors::IncompleteResult; 11 | use crate::core::errors::StreamError; 12 | use crate::core::packet::Packet; 13 | 14 | /// A stream/sink interface to underlying websocket frame stream. Encodes/decodes bilibili live packets. 15 | pub struct CodecStream { 16 | /// underlying tungstenite stream 17 | stream: T, 18 | /// rx buffer 19 | read_buffer: Vec, 20 | } 21 | 22 | impl CodecStream { 23 | /// Convert a tungstenite stream into a [`CodecStream`](CodecStream). 24 | /// 25 | /// You may want to use `connect` or `connect_with_retry` in [`connect`](crate::connect) module instead. 26 | pub const fn new(stream: T) -> Self { 27 | Self { 28 | stream, 29 | read_buffer: vec![], 30 | } 31 | } 32 | } 33 | 34 | impl Stream for CodecStream 35 | where 36 | T: Stream> + Unpin, 37 | { 38 | type Item = Result>; 39 | 40 | fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 41 | loop { 42 | // poll the underlying websocket stream 43 | if let Some(msg) = ready!(Pin::new(&mut self.stream).poll_next(cx)) { 44 | match msg { 45 | Ok(msg) => { 46 | if msg.is_binary() { 47 | // append data to the end of the buffer 48 | self.read_buffer.extend(msg.into_data()); 49 | // parse the message 50 | match Packet::parse(&self.read_buffer) { 51 | IncompleteResult::Ok((remaining, pack)) => { 52 | debug!("packet parsed, {} bytes remaining", remaining.len()); 53 | 54 | // remove parsed bytes 55 | let consume_len = self.read_buffer.len() - remaining.len(); 56 | drop(self.read_buffer.drain(..consume_len)); 57 | 58 | return Poll::Ready(Some(Ok(pack))); 59 | } 60 | IncompleteResult::Incomplete(needed) => { 61 | debug!("incomplete packet, {:?} needed", needed); 62 | } 63 | IncompleteResult::Err(e) => { 64 | warn!("error occurred when parsing incoming packet"); 65 | return Poll::Ready(Some(Err(e.into()))); 66 | } 67 | } 68 | } else { 69 | debug!("not a binary message, dropping"); 70 | } 71 | } 72 | Err(e) => { 73 | // underlying websocket error, closing connection 74 | warn!("error occurred when receiving message: {:?}", e); 75 | return Poll::Ready(None); 76 | } 77 | } 78 | } else { 79 | // underlying websocket closing 80 | return Poll::Ready(None); 81 | } 82 | } 83 | } 84 | } 85 | 86 | impl Sink for CodecStream 87 | where 88 | T: Sink + Unpin, 89 | { 90 | type Error = StreamError; 91 | 92 | fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 93 | Pin::new(&mut self.stream) 94 | .poll_ready(cx) 95 | .map_err(StreamError::from_ws_error) 96 | } 97 | 98 | fn start_send(mut self: Pin<&mut Self>, item: Packet) -> Result<(), Self::Error> { 99 | Pin::new(&mut self.stream) 100 | .start_send(Message::binary(item.encode())) 101 | .map_err(StreamError::from_ws_error) 102 | } 103 | 104 | fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 105 | Pin::new(&mut self.stream) 106 | .poll_flush(cx) 107 | .map_err(StreamError::from_ws_error) 108 | } 109 | 110 | fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 111 | Pin::new(&mut self.stream) 112 | .poll_close(cx) 113 | .map_err(StreamError::from_ws_error) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /bililive/src/stream/mod.rs: -------------------------------------------------------------------------------- 1 | //! Bilibili live stream. 2 | pub use codec::CodecStream; 3 | 4 | mod codec; 5 | #[cfg(test)] 6 | mod tests; 7 | -------------------------------------------------------------------------------- /bililive/src/stream/tests.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use async_tungstenite::tungstenite::Error as WsError; 4 | use futures::{Future, Sink, SinkExt, Stream, StreamExt}; 5 | 6 | use crate::builder::tests::build_real_config; 7 | use crate::core::errors::StreamError; 8 | use crate::core::packet::{Operation, Packet, Protocol}; 9 | use crate::core::retry::RetryConfig; 10 | 11 | async fn must_future_timeout(dur: Duration, fut: impl Future) { 12 | if cfg!(feature = "tokio") { 13 | #[cfg(feature = "tokio")] 14 | assert!( 15 | tokio::time::timeout(dur, fut).await.is_err(), 16 | "future not timeout" 17 | ); 18 | } else { 19 | #[cfg(feature = "async-std")] 20 | assert!( 21 | async_std::future::timeout(dur, fut).await.is_err(), 22 | "future not timeout" 23 | ); 24 | }; 25 | } 26 | 27 | async fn test_stream( 28 | mut stream: impl Stream>> 29 | + Sink> 30 | + Unpin, 31 | ) { 32 | let mut msg_count = 0; 33 | 34 | let stream_try = async { 35 | while let Some(msg) = stream.next().await { 36 | msg.expect("stream error"); 37 | msg_count += 1; 38 | } 39 | }; 40 | // err means timeout indicating there's no early stop on stream 41 | must_future_timeout(Duration::from_secs(3), stream_try).await; 42 | 43 | stream 44 | .send(Packet::new(Operation::HeartBeat, Protocol::Json, vec![])) 45 | .await 46 | .expect("sink error"); 47 | let mut hb_resp_received = false; 48 | let stream_try = async { 49 | while let Some(msg) = stream.next().await { 50 | let msg = msg.expect("stream error"); 51 | if msg.op() == Operation::HeartBeatResponse { 52 | hb_resp_received = true; 53 | } 54 | } 55 | }; 56 | // err means timeout indicating there's no early stop on stream 57 | must_future_timeout(Duration::from_secs(1), stream_try).await; 58 | assert!(hb_resp_received, "no heart beat response received"); 59 | 60 | stream.close().await.expect("unable to close stream"); 61 | } 62 | 63 | async fn test_stream_heartbeat( 64 | mut stream: impl Stream>> 65 | + Sink> 66 | + Unpin, 67 | ) { 68 | let stream_try = async { 69 | while let Some(Ok(_)) = stream.next().await {} 70 | panic!("connection closed (heartbeat not sent)"); 71 | }; 72 | // err means timeout indicating there's no early stop on stream 73 | must_future_timeout(Duration::from_secs(120), stream_try).await; 74 | 75 | stream.close().await.expect("unable to close stream"); 76 | } 77 | 78 | #[cfg(feature = "tokio")] 79 | #[tokio::test(flavor = "multi_thread", worker_threads = 6)] 80 | async fn must_stream_tokio() { 81 | let config = build_real_config(true).await; 82 | 83 | let stream = crate::connect::tokio::connect(config) 84 | .await 85 | .expect("unable to establish connection"); 86 | test_stream(stream).await; 87 | } 88 | 89 | #[cfg(feature = "tokio")] 90 | #[tokio::test(flavor = "multi_thread", worker_threads = 6)] 91 | async fn must_retry_stream_tokio() { 92 | let config = build_real_config(false).await; 93 | 94 | let stream = crate::connect::tokio::connect_with_retry(config, RetryConfig::default()) 95 | .await 96 | .expect("unable to establish connection"); 97 | test_stream(stream).await; 98 | } 99 | 100 | #[cfg(feature = "async-std")] 101 | #[async_std::test] 102 | async fn must_stream_async_std() { 103 | let config = build_real_config(true).await; 104 | 105 | let stream = crate::connect::async_std::connect(config) 106 | .await 107 | .expect("unable to establish connection"); 108 | test_stream(stream).await; 109 | } 110 | 111 | #[cfg(feature = "async-std")] 112 | #[async_std::test] 113 | async fn must_retry_async_std() { 114 | let config = build_real_config(false).await; 115 | 116 | let stream = crate::connect::async_std::connect_with_retry(config, RetryConfig::default()) 117 | .await 118 | .expect("unable to establish connection"); 119 | test_stream(stream).await; 120 | } 121 | 122 | #[cfg(feature = "tokio")] 123 | #[tokio::test(flavor = "multi_thread", worker_threads = 6)] 124 | async fn must_hb_tokio() { 125 | if option_env!("FAST_TEST").is_some() { 126 | return; 127 | } 128 | 129 | let config = build_real_config(true).await; 130 | 131 | let stream = crate::connect::tokio::connect(config) 132 | .await 133 | .expect("unable to establish connection"); 134 | test_stream_heartbeat(stream).await; 135 | } 136 | 137 | #[cfg(feature = "async-std")] 138 | #[async_std::test] 139 | async fn must_hb_async_std() { 140 | if option_env!("FAST_TEST").is_some() { 141 | return; 142 | } 143 | 144 | let config = build_real_config(true).await; 145 | 146 | let stream = crate::connect::async_std::connect(config) 147 | .await 148 | .expect("unable to establish connection"); 149 | test_stream_heartbeat(stream).await; 150 | } 151 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "stable" 3 | profile = "default" --------------------------------------------------------------------------------