Adversarial unsanitized input may cause MongoDB Rust Driver to issue unintended commands.
Description
Incorrect handling of certain string inputs may result in MongoDB Rust driver constructing unintended server commands. This may cause unexpected application behavior including data modification. This issue affects MongoDB Rust Driver 2.0 versions prior to 2.8.2
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
CVE-2024-6382: BSON integer overflow in MongoDB Rust driver before 2.8.2 allows unintended server commands and potential data modification.
Vulnerability
CVE-2024-6382 is an integer overflow vulnerability in the MongoDB Rust driver (versions 2.0 up to 2.8.2). The bug occurs in how the driver handles certain string inputs, specifically when computing the size of BSON array entries. The function array_entry_size_bytes previously performed an unchecked cast from usize to u64 (e.g., doc_len as u64), which could overflow and produce an incorrect size value [3][4].
Exploitation
An attacker could supply crafted string inputs that cause the driver to miscalculate array entry sizes. Because the driver uses this size calculation when constructing server commands, the overflow could lead to sending malformed or unintended BSON data to the MongoDB server. The vulnerability can be triggered without authentication if the application accepts untrusted input in BSON array contexts, though the exact attack surface depends on how the driver is used in each application [1][2].
Impact
Successful exploitation may cause the MongoDB server to execute commands different from what the application intended, potentially resulting in unexpected behavior, data corruption, or unauthorized data modification. The fix patched in version 2.8.2 introduces a Checked arithmetic wrapper to ensure all size calculations are safe from overflow [3][4].
Mitigation
Users should update the MongoDB Rust driver to version 2.8.2 or later. As stated in the NVD entry, this issue affects driver version 2.0 through 2.8.1. The official GitHub repository and advisory recommend always depending on the latest 2.x release [1][2].
AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
mongodbcrates.io | >= 2.0.0, < 2.8.2 | 2.8.2 |
Affected products
2- MongoDB Inc/MongoDB Rust Driverv5cpe:2.3:a:mongodb:rust-driver:2.0.0:alpha:*:*:*:mongodb:*:*Range: 2.0
Patches
28eac3bc6dc37RUST-1881 (#1047)
45 files changed · +1051 −3870
.evergreen/compile-only.sh+1 −1 modified@@ -9,7 +9,7 @@ source ./.evergreen/env.sh if [ "$RUST_VERSION" != "" ]; then rustup toolchain install $RUST_VERSION TOOLCHAIN="+${RUST_VERSION}" - cp .evergreen/MSRV-Cargo.lock Cargo.lock + patch Cargo.toml .evergreen/MSRV-Cargo.toml.diff fi source ./.evergreen/feature-combinations.sh
.evergreen/MSRV-Cargo.lock+0 −2794 removed@@ -1,2794 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7d5a2cecb58716e47d67d5703a249964b14c7be1ec3cad3affc295b2d1c35d" -dependencies = [ - "cfg-if", - "getrandom", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "0.7.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" -dependencies = [ - "memchr", -] - -[[package]] -name = "anyhow" -version = "1.0.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" -dependencies = [ - "backtrace", -] - -[[package]] -name = "approx" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" -dependencies = [ - "num-traits", -] - -[[package]] -name = "array-init" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23589ecb866b460d3a0f1278834750268c607e8e28a1b982c907219f3178cd72" -dependencies = [ - "nodrop", -] - -[[package]] -name = "async-attributes" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" -dependencies = [ - "quote", - "syn 1.0.98", -] - -[[package]] -name = "async-channel" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" -dependencies = [ - "concurrent-queue 1.2.2", - "event-listener", - "futures-core", -] - -[[package]] -name = "async-executor" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" -dependencies = [ - "async-lock", - "async-task", - "concurrent-queue 2.3.0", - "fastrand", - "futures-lite", - "slab", -] - -[[package]] -name = "async-global-executor" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5262ed948da60dd8956c6c5aca4d4163593dddb7b32d73267c93dab7b2e98940" -dependencies = [ - "async-channel", - "async-executor", - "async-io", - "async-lock", - "blocking", - "futures-lite", - "num_cpus", - "once_cell", -] - -[[package]] -name = "async-io" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5e18f61464ae81cde0a23e713ae8fd299580c54d697a35820cfd0625b8b0e07" -dependencies = [ - "concurrent-queue 1.2.2", - "futures-lite", - "libc", - "log", - "once_cell", - "parking", - "polling", - "slab", - "socket2", - "waker-fn", - "winapi", -] - -[[package]] -name = "async-lock" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" -dependencies = [ - "event-listener", -] - -[[package]] -name = "async-process" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2c06e30a24e8c78a3987d07f0930edf76ef35e027e7bdb063fccafdad1f60c" -dependencies = [ - "async-io", - "blocking", - "cfg-if", - "event-listener", - "futures-lite", - "libc", - "once_cell", - "signal-hook", - "winapi", -] - -[[package]] -name = "async-std" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" -dependencies = [ - "async-attributes", - "async-channel", - "async-global-executor", - "async-io", - "async-lock", - "async-process", - "crossbeam-utils", - "futures-channel", - "futures-core", - "futures-io", - "futures-lite", - "gloo-timers", - "kv-log-macro", - "log", - "memchr", - "once_cell", - "pin-project-lite", - "pin-utils", - "slab", - "wasm-bindgen-futures", -] - -[[package]] -name = "async-std-resolver" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f2f8a4a203be3325981310ab243a28e6e4ea55b6519bffce05d41ab60e09ad8" -dependencies = [ - "async-std", - "async-trait", - "futures-io", - "futures-util", - "pin-utils", - "socket2", - "trust-dns-resolver", -] - -[[package]] -name = "async-stream" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" -dependencies = [ - "async-stream-impl", - "futures-core", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.98", -] - -[[package]] -name = "async-task" -version = "4.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" - -[[package]] -name = "async-trait" -version = "0.1.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.98", -] - -[[package]] -name = "atomic-waker" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "backtrace" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide 0.7.1", - "object", - "rustc-demangle", -] - -[[package]] -name = "base64" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "block-buffer" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" -dependencies = [ - "generic-array", -] - -[[package]] -name = "blocking" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc" -dependencies = [ - "async-channel", - "async-task", - "atomic-waker", - "fastrand", - "futures-lite", - "once_cell", -] - -[[package]] -name = "bson" -version = "2.6.0" -source = "git+https://github.com/mongodb/bson-rust?branch=main#b243db19b74745fcf52eacd9e4d34077186186d5" -dependencies = [ - "ahash", - "base64", - "bitvec", - "chrono", - "hex", - "indexmap", - "js-sys", - "once_cell", - "rand", - "serde", - "serde_bytes", - "serde_json", - "serde_with", - "time", - "uuid 0.8.2", - "uuid 1.1.2", -] - -[[package]] -name = "bumpalo" -version = "3.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" - -[[package]] -name = "bytes" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0b3de4a0c5e67e16066a0715723abd91edc2f9001d09c46e1dca929351e130e" - -[[package]] -name = "cache-padded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" - -[[package]] -name = "cc" -version = "1.0.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" -dependencies = [ - "jobserver", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" -dependencies = [ - "libc", - "num-integer", - "num-traits", - "winapi", -] - -[[package]] -name = "concurrent-queue" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" -dependencies = [ - "cache-padded", -] - -[[package]] -name = "concurrent-queue" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - -[[package]] -name = "cpufeatures" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "ctor" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c" -dependencies = [ - "quote", - "syn 1.0.98", -] - -[[package]] -name = "ctrlc" -version = "3.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b37feaa84e6861e00a1f5e5aa8da3ee56d605c9992d33e082786754828e20865" -dependencies = [ - "nix", - "winapi", -] - -[[package]] -name = "darling" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 1.0.98", -] - -[[package]] -name = "darling_macro" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" -dependencies = [ - "darling_core", - "quote", - "syn 1.0.98", -] - -[[package]] -name = "data-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.98", -] - -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "rustc_version 0.4.0", - "syn 1.0.98", -] - -[[package]] -name = "diff" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" - -[[package]] -name = "digest" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" -dependencies = [ - "block-buffer", - "crypto-common", - "subtle", -] - -[[package]] -name = "either" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" - -[[package]] -name = "encoding_rs" -version = "0.8.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "enum-as-inner" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21cdad81446a7f7dc43f6a77409efeb9733d2fa65553efef6018ef257c959b73" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 1.0.98", -] - -[[package]] -name = "event-listener" -version = "2.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" - -[[package]] -name = "fastrand" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" -dependencies = [ - "instant", -] - -[[package]] -name = "flate2" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" -dependencies = [ - "crc32fast", - "miniz_oxide 0.5.3", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" -dependencies = [ - "matches", - "percent-encoding", -] - -[[package]] -name = "function_name" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef632c665dc6e2b99ffa4d913f7160bd902c4d3e4cb732d81dc3d221f848512" -dependencies = [ - "function_name-proc-macro", -] - -[[package]] -name = "function_name-proc-macro" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "569d2238870f92cff64fc810013b61edaf446ebcfba36b649b96bc5b4078328a" -dependencies = [ - "proc-macro-crate", - "quote", - "syn 1.0.98", -] - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futures" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" - -[[package]] -name = "futures-executor" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" - -[[package]] -name = "futures-lite" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - -[[package]] -name = "futures-macro" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.98", -] - -[[package]] -name = "futures-sink" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" - -[[package]] -name = "futures-task" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" - -[[package]] -name = "futures-util" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "gimli" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" - -[[package]] -name = "gloo-timers" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "h2" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "heck" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "home" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654" -dependencies = [ - "winapi", -] - -[[package]] -name = "hostname" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" -dependencies = [ - "libc", - "match_cfg", - "winapi", -] - -[[package]] -name = "http" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" - -[[package]] -name = "httpdate" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" - -[[package]] -name = "hyper" -version = "0.14.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" -dependencies = [ - "http", - "hyper", - "rustls 0.20.6", - "tokio", - "tokio-rustls 0.23.4", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "indexmap" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" -dependencies = [ - "autocfg", - "hashbrown", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "ipconfig" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "723519edce41262b05d4143ceb95050e4c614f483e78e9fd9e39a8275a84ad98" -dependencies = [ - "socket2", - "widestring", - "winapi", - "winreg 0.7.0", -] - -[[package]] -name = "ipnet" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" - -[[package]] -name = "itoa" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" - -[[package]] -name = "jobserver" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" -dependencies = [ - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "kv-log-macro" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" -dependencies = [ - "log", -] - -[[package]] -name = "lambda_runtime" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a81840726d481d20b99a9ce87430f644e9590cb77715e1e66c5f4432c9b586" -dependencies = [ - "async-stream", - "bytes", - "futures", - "http", - "hyper", - "lambda_runtime_api_client", - "serde", - "serde_json", - "tokio", - "tokio-stream", - "tower", - "tracing", -] - -[[package]] -name = "lambda_runtime_api_client" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b54698c666ffe503cb51fa66e4567e53e806128a10359de7095999d925a771ed" -dependencies = [ - "http", - "hyper", - "tokio", - "tower-service", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.149" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" - -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - -[[package]] -name = "lock_api" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" -dependencies = [ - "value-bag", -] - -[[package]] -name = "lru-cache" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" -dependencies = [ - "linked-hash-map", -] - -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - -[[package]] -name = "matches" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" - -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" - -[[package]] -name = "md-5" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582" -dependencies = [ - "digest", -] - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - -[[package]] -name = "mime" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" - -[[package]] -name = "miniz_oxide" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" -dependencies = [ - "adler", -] - -[[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", -] - -[[package]] -name = "mio" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys", -] - -[[package]] -name = "mongocrypt" -version = "0.1.2" -source = "git+https://github.com/mongodb/libmongocrypt-rust.git?branch=main" -dependencies = [ - "bson", - "mongocrypt-sys", - "once_cell", - "serde", -] - -[[package]] -name = "mongocrypt-sys" -version = "0.1.2+1.8.0-alpha1" -source = "git+https://github.com/mongodb/libmongocrypt-rust.git?branch=main#eee5a9817cdfb92204f6167ade5064f540e8b9e9" - -[[package]] -name = "mongodb" -version = "2.7.0" -dependencies = [ - "anyhow", - "approx", - "async-executor", - "async-std", - "async-std-resolver", - "async-trait", - "backtrace", - "base64", - "bitflags", - "bson", - "chrono", - "ctrlc", - "derivative", - "derive_more", - "flate2", - "function_name", - "futures", - "futures-core", - "futures-executor", - "futures-io", - "futures-util", - "hex", - "hmac", - "home", - "lambda_runtime", - "lazy_static", - "log", - "md-5", - "mongocrypt", - "num_cpus", - "openssl", - "openssl-probe", - "pbkdf2", - "percent-encoding", - "pretty_assertions", - "rand", - "rayon", - "regex", - "reqwest", - "rustc_version_runtime", - "rustls 0.21.7", - "rustls-pemfile", - "semver 1.0.12", - "serde", - "serde-hex", - "serde_bytes", - "serde_json", - "serde_with", - "sha-1", - "sha2", - "snap", - "socket2", - "stringprep", - "strsim", - "take_mut", - "thiserror", - "time", - "tokio", - "tokio-openssl", - "tokio-rustls 0.24.1", - "tokio-util", - "tracing", - "tracing-subscriber", - "trust-dns-proto", - "trust-dns-resolver", - "typed-builder", - "uuid 1.1.2", - "webpki-roots 0.25.2", - "zstd", -] - -[[package]] -name = "nix" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" -dependencies = [ - "bitflags", - "cfg-if", - "libc", -] - -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "num_threads" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" -dependencies = [ - "libc", -] - -[[package]] -name = "object" -version = "0.32.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "openssl" -version = "0.10.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "618febf65336490dfcf20b73f885f5651a0c89c64c2d4a8c3662585a70bf5bd0" -dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.98", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5f9bd0c2710541a3cda73d6f9ac4f1b240de4ae261065d309dbe73d9dceb42f" -dependencies = [ - "autocfg", - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "output_vt100" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" -dependencies = [ - "winapi", -] - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "parking" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec 1.9.0", - "windows-sys", -] - -[[package]] -name = "pbkdf2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" -dependencies = [ - "digest", -] - -[[package]] -name = "percent-encoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" - -[[package]] -name = "pin-project" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.98", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" - -[[package]] -name = "polling" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" -dependencies = [ - "cfg-if", - "libc", - "log", - "wepoll-ffi", - "winapi", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - -[[package]] -name = "pretty_assertions" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" -dependencies = [ - "ctor", - "diff", - "output_vt100", - "yansi", -] - -[[package]] -name = "proc-macro-crate" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" -dependencies = [ - "toml", -] - -[[package]] -name = "proc-macro2" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quote" -version = "1.0.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rayon" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" -dependencies = [ - "autocfg", - "crossbeam-deque", - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", -] - -[[package]] -name = "redox_syscall" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534cfe58d6a18cc17120fbf4635d53d14691c1fe4d951064df9bd326178d7d5a" -dependencies = [ - "bitflags", -] - -[[package]] -name = "regex" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" - -[[package]] -name = "reqwest" -version = "0.11.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75aa69a3f06bbcc66ede33af2af253c6f7a86b1ca0033f60c580a27074fbf92" -dependencies = [ - "base64", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-rustls", - "ipnet", - "js-sys", - "lazy_static", - "log", - "mime", - "percent-encoding", - "pin-project-lite", - "rustls 0.20.6", - "rustls-pemfile", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-rustls 0.23.4", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots 0.22.4", - "winreg 0.10.1", -] - -[[package]] -name = "resolv-conf" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" -dependencies = [ - "hostname", - "quick-error", -] - -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver 1.0.12", -] - -[[package]] -name = "rustc_version_runtime" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d31b7153270ebf48bf91c65ae5b0c00e749c4cfad505f66530ac74950249582f" -dependencies = [ - "rustc_version 0.2.3", - "semver 0.9.0", -] - -[[package]] -name = "rustls" -version = "0.20.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" -dependencies = [ - "log", - "ring", - "sct", - "webpki", -] - -[[package]] -name = "rustls" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" -dependencies = [ - "log", - "ring", - "rustls-webpki", - "sct", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" -dependencies = [ - "base64", -] - -[[package]] -name = "rustls-webpki" -version = "0.101.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "ryu" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "sct" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1" - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - -[[package]] -name = "serde" -version = "1.0.140" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-hex" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca37e3e4d1b39afd7ff11ee4e947efae85adfddf4841787bfa47c470e96dc26d" -dependencies = [ - "array-init", - "serde", - "smallvec 0.6.14", -] - -[[package]] -name = "serde_bytes" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212e73464ebcde48d723aa02eb270ba62eff38a9b732df31f33f1b4e145f3a54" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.140" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.98", -] - -[[package]] -name = "serde_json" -version = "1.0.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" -dependencies = [ - "indexmap", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_with" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" -dependencies = [ - "serde", - "serde_with_macros", -] - -[[package]] -name = "serde_with_macros" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 1.0.98", -] - -[[package]] -name = "sha-1" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "signal-hook" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" -dependencies = [ - "libc", -] - -[[package]] -name = "slab" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "0.6.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" -dependencies = [ - "maybe-uninit", -] - -[[package]] -name = "smallvec" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" - -[[package]] -name = "snap" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45456094d1983e2ee2a18fdfebce3189fa451699d0502cb8e3b49dba5ba41451" - -[[package]] -name = "socket2" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "stringprep" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "syn" -version = "1.0.98" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "take_mut" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "thiserror" -version = "1.0.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.98", -] - -[[package]] -name = "thread_local" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "time" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd" -dependencies = [ - "itoa", - "libc", - "num_threads", - "time-macros", -] - -[[package]] -name = "time-macros" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" - -[[package]] -name = "tokio" -version = "1.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a8325f63a7d4774dd041e363b2409ed1c5cbbd0f867795e661df066b2b0a581" -dependencies = [ - "autocfg", - "bytes", - "libc", - "memchr", - "mio", - "num_cpus", - "once_cell", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "winapi", -] - -[[package]] -name = "tokio-macros" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.98", -] - -[[package]] -name = "tokio-openssl" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08f9ffb7809f1b20c1b398d92acf4cc719874b3b2b2d9ea2f09b4a80350878a" -dependencies = [ - "futures-util", - "openssl", - "openssl-sys", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.23.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" -dependencies = [ - "rustls 0.20.6", - "tokio", - "webpki", -] - -[[package]] -name = "tokio-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" -dependencies = [ - "rustls 0.21.7", - "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6edf2d6bc038a43d31353570e27270603f4648d18f5ed10c0e179abe43255af" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" -dependencies = [ - "bytes", - "futures-core", - "futures-io", - "futures-sink", - "pin-project-lite", - "tokio", - "tracing", -] - -[[package]] -name = "toml" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" -dependencies = [ - "serde", -] - -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "pin-project", - "pin-project-lite", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-layer" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.38", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" -dependencies = [ - "nu-ansi-term", - "sharded-slab", - "smallvec 1.9.0", - "thread_local", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "trust-dns-proto" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c31f240f59877c3d4bb3b3ea0ec5a6a0cff07323580ff8c7a605cd7d08b255d" -dependencies = [ - "async-trait", - "cfg-if", - "data-encoding", - "enum-as-inner", - "futures-channel", - "futures-io", - "futures-util", - "idna", - "ipnet", - "lazy_static", - "log", - "rand", - "smallvec 1.9.0", - "thiserror", - "tinyvec", - "tokio", - "url", -] - -[[package]] -name = "trust-dns-resolver" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ba72c2ea84515690c9fcef4c6c660bb9df3036ed1051686de84605b74fd558" -dependencies = [ - "cfg-if", - "futures-util", - "ipconfig", - "lazy_static", - "log", - "lru-cache", - "parking_lot", - "resolv-conf", - "smallvec 1.9.0", - "thiserror", - "tokio", - "trust-dns-proto", -] - -[[package]] -name = "try-lock" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" - -[[package]] -name = "typed-builder" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89851716b67b937e393b3daa8423e67ddfc4bbbf1654bcf05488e95e0828db0c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.98", -] - -[[package]] -name = "typenum" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" - -[[package]] -name = "unicode-bidi" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" - -[[package]] -name = "unicode-ident" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" - -[[package]] -name = "unicode-normalization" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "url" -version = "2.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" -dependencies = [ - "form_urlencoded", - "idna", - "matches", - "percent-encoding", -] - -[[package]] -name = "uuid" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" -dependencies = [ - "getrandom", - "serde", -] - -[[package]] -name = "uuid" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f" -dependencies = [ - "getrandom", - "serde", -] - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "value-bag" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a72e1902dde2bd6441347de2b70b7f5d59bf157c6c62f0c44572607a1d55bbe" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "waker-fn" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" - -[[package]] -name = "want" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -dependencies = [ - "log", - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" -dependencies = [ - "bumpalo", - "lazy_static", - "log", - "proc-macro2", - "quote", - "syn 1.0.98", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de9a9cec1733468a8c657e57fa2413d2ae2c0129b95e87c5b72b8ace4d13f31f" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -c ... [truncated]
.evergreen/MSRV-Cargo.toml.diff+18 −0 added@@ -0,0 +1,18 @@ +95c95 +< bson = "2.8.0" +--- +> bson = "=2.8.0" +103a104 +> hashbrown = "=0.12.3" +166c167 +< version = "1.17.0" +--- +> version = "=1.18.0" +193c194 +< serde_json = "1.0.64" +--- +> serde_json = "=1.0.64" +195c196 +< time = "0.3.9" +--- +> time = "=0.3.9"
src/bson_util.rs+6 −3 modified@@ -5,12 +5,14 @@ use std::{ use crate::{ bson::{Bson, Document, RawArrayBuf, RawBson, RawBsonRef, RawDocumentBuf}, + checked::Checked, error::{ErrorKind, Result}, runtime::SyncLittleEndianRead, }; /// Coerce numeric types into an `i64` if it would be lossless to do so. If this Bson is not numeric /// or the conversion would be lossy (e.g. 1.5 -> 1), this returns `None`. +#[allow(clippy::cast_possible_truncation)] pub(crate) fn get_int(val: &Bson) -> Option<i64> { match *val { Bson::Int32(i) => Some(i64::from(i)), @@ -33,6 +35,7 @@ pub(crate) fn get_int_raw(val: RawBsonRef<'_>) -> Option<i64> { /// Coerce numeric types into an `u64` if it would be lossless to do so. If this Bson is not numeric /// or the conversion would be lossy (e.g. 1.5 -> 1), this returns `None`. +#[allow(clippy::cast_possible_truncation)] pub(crate) fn get_u64(val: &Bson) -> Option<u64> { match *val { Bson::Int32(i) => u64::try_from(i).ok(), @@ -89,18 +92,18 @@ pub(crate) fn update_document_check(update: &Document) -> Result<()> { } /// The size in bytes of the provided document's entry in a BSON array at the given index. -pub(crate) fn array_entry_size_bytes(index: usize, doc_len: usize) -> u64 { +pub(crate) fn array_entry_size_bytes(index: usize, doc_len: usize) -> Result<usize> { // * type (1 byte) // * number of decimal digits in key // * null terminator for the key (1 byte) // * size of value - 1 + num_decimal_digits(index) + 1 + doc_len as u64 + (Checked::new(1) + num_decimal_digits(index) + 1 + doc_len).get() } /// The number of digits in `n` in base 10. /// Useful for calculating the size of an array entry in BSON. -fn num_decimal_digits(mut n: usize) -> u64 { +fn num_decimal_digits(mut n: usize) -> usize { let mut digits = 0; loop {
src/checked.rs+502 −0 added@@ -0,0 +1,502 @@ +// Modified from https://github.com/zeta12ti/Checked/blob/master/src/num.rs +// Original license: +// MIT License +// +// Copyright (c) 2017 zeta12ti +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +use std::{cmp::Ordering, convert::TryFrom, fmt, ops::*}; + +/// The Checked type. See the [module level documentation for more.](index.html) +#[derive(PartialEq, Eq, Clone, Copy, Hash)] +pub struct Checked<T>(pub Option<T>); + +impl<T> Checked<T> { + /// Creates a new Checked instance from some sort of integer. + #[inline] + pub fn new(x: T) -> Checked<T> { + Checked(Some(x)) + } + + pub fn try_from<F>(value: F) -> crate::error::Result<Self> + where + T: TryFrom<F>, + T::Error: std::fmt::Display, + { + value + .try_into() + .map(|v| Self(Some(v))) + .map_err(|e| crate::error::Error::invalid_argument(format! {"{}", e})) + } + + pub fn get(self) -> crate::error::Result<T> { + self.0 + .ok_or_else(|| crate::error::Error::invalid_argument("checked arithmetic failure")) + } + + pub fn try_into<F>(self) -> crate::error::Result<F> + where + T: TryInto<F>, + T::Error: std::fmt::Display, + { + self.get().and_then(|v| { + v.try_into() + .map_err(|e| crate::error::Error::invalid_argument(format!("{}", e))) + }) + } +} + +// The derived Default only works if T has Default +// Even though this is what it would be anyway +// May change this to T's default (if it has one) +impl<T> Default for Checked<T> { + #[inline] + fn default() -> Checked<T> { + Checked(None) + } +} + +impl<T: fmt::Debug> fmt::Debug for Checked<T> { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match **self { + Some(ref x) => x.fmt(f), + None => "overflow".fmt(f), + } + } +} + +impl<T: fmt::Display> fmt::Display for Checked<T> { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match **self { + Some(ref x) => x.fmt(f), + None => "overflow".fmt(f), + } + } +} + +// I'd like to do +// `impl<T, U> From<U> where T: From<U> for Checked<T>`` +// in the obvious way, but that "conflicts" with the default `impl From<T> for T`. +// This would subsume both the below Froms since Option has the right From impl. +impl<T> From<T> for Checked<T> { + #[inline] + fn from(x: T) -> Checked<T> { + Checked(Some(x)) + } +} + +impl<T> From<Option<T>> for Checked<T> { + #[inline] + fn from(x: Option<T>) -> Checked<T> { + Checked(x) + } +} + +impl<T> Deref for Checked<T> { + type Target = Option<T>; + + #[inline] + fn deref(&self) -> &Option<T> { + &self.0 + } +} + +impl<T> DerefMut for Checked<T> { + #[inline] + fn deref_mut(&mut self) -> &mut Option<T> { + &mut self.0 + } +} + +impl<T: PartialOrd> PartialOrd for Checked<T> { + fn partial_cmp(&self, other: &Checked<T>) -> Option<Ordering> { + // I'm not really sure why we can't match **self etc. here. + // Even with refs everywhere it complains + // Note what happens in this implementation: + // we take the reference self, and call deref (the method) on it + // By Deref coercion, self gets derefed to a Checked<T> + // Now Checked<T>'s deref gets called, returning a &Option<T> + // That's what gets matched + match (self.deref(), other.deref()) { + (Some(x), Some(y)) => PartialOrd::partial_cmp(x, y), + _ => None, + } + } +} + +// implements the unary operator `op &T` +// based on `op T` where `T` is expected to be `Copy`able +macro_rules! forward_ref_unop { + (impl $imp:ident, $method:ident for $t:ty {}) => { + impl<'a> $imp for &'a $t { + type Output = <$t as $imp>::Output; + + #[inline] + fn $method(self) -> <$t as $imp>::Output { + $imp::$method(*self) + } + } + }; +} + +// implements binary operators "&T op U", "T op &U", "&T op &U" +// based on "T op U" where T and U are expected to be `Copy`able +macro_rules! forward_ref_binop { + (impl $imp:ident, $method:ident for $t:ty, $u:ty {}) => { + impl<'a> $imp<$u> for &'a $t { + type Output = <$t as $imp<$u>>::Output; + + #[inline] + fn $method(self, other: $u) -> <$t as $imp<$u>>::Output { + $imp::$method(*self, other) + } + } + + impl<'a> $imp<&'a $u> for $t { + type Output = <$t as $imp<$u>>::Output; + + #[inline] + fn $method(self, other: &'a $u) -> <$t as $imp<$u>>::Output { + $imp::$method(self, *other) + } + } + + impl<'a, 'b> $imp<&'a $u> for &'b $t { + type Output = <$t as $imp<$u>>::Output; + + #[inline] + fn $method(self, other: &'a $u) -> <$t as $imp<$u>>::Output { + $imp::$method(*self, *other) + } + } + }; +} + +macro_rules! impl_sh { + ($t:ident, $f:ident) => { + impl Shl<Checked<$f>> for Checked<$t> { + type Output = Checked<$t>; + + fn shl(self, other: Checked<$f>) -> Checked<$t> { + match (*self, *other) { + (Some(x), Some(y)) => Checked(x.checked_shl(y)), + _ => Checked(None), + } + } + } + + impl Shl<$f> for Checked<$t> { + type Output = Checked<$t>; + + fn shl(self, other: $f) -> Checked<$t> { + match *self { + Some(x) => Checked(x.checked_shl(other)), + None => Checked(None), + } + } + } + + forward_ref_binop! { impl Shl, shl for Checked<$t>, Checked<$f> {} } + forward_ref_binop! { impl Shl, shl for Checked<$t>, $f {} } + + impl ShlAssign<$f> for Checked<$t> { + #[inline] + fn shl_assign(&mut self, other: $f) { + *self = *self << other; + } + } + + impl ShlAssign<Checked<$f>> for Checked<$t> { + #[inline] + fn shl_assign(&mut self, other: Checked<$f>) { + *self = *self << other; + } + } + + impl Shr<Checked<$f>> for Checked<$t> { + type Output = Checked<$t>; + + fn shr(self, other: Checked<$f>) -> Checked<$t> { + match (*self, *other) { + (Some(x), Some(y)) => Checked(x.checked_shr(y)), + _ => Checked(None), + } + } + } + + impl Shr<$f> for Checked<$t> { + type Output = Checked<$t>; + + fn shr(self, other: $f) -> Checked<$t> { + match *self { + Some(x) => Checked(x.checked_shr(other)), + None => Checked(None), + } + } + } + + forward_ref_binop! { impl Shr, shr for Checked<$t>, Checked<$f> {} } + forward_ref_binop! { impl Shr, shr for Checked<$t>, $f {} } + + impl ShrAssign<$f> for Checked<$t> { + #[inline] + fn shr_assign(&mut self, other: $f) { + *self = *self >> other; + } + } + + impl ShrAssign<Checked<$f>> for Checked<$t> { + #[inline] + fn shr_assign(&mut self, other: Checked<$f>) { + *self = *self >> other; + } + } + }; +} + +macro_rules! impl_sh_reverse { + ($t:ident, $f:ident) => { + impl Shl<Checked<$t>> for $f { + type Output = Checked<$f>; + + fn shl(self, other: Checked<$t>) -> Checked<$f> { + match *other { + Some(x) => Checked(self.checked_shl(x)), + None => Checked(None), + } + } + } + + forward_ref_binop! { impl Shl, shl for $f, Checked<$t> {} } + + impl Shr<Checked<$t>> for $f { + type Output = Checked<$f>; + + fn shr(self, other: Checked<$t>) -> Checked<$f> { + match *other { + Some(x) => Checked(self.checked_shr(x)), + None => Checked(None), + } + } + } + + forward_ref_binop! { impl Shr, shr for $f, Checked<$t> {} } + }; +} + +macro_rules! impl_sh_all { + ($($t:ident)*) => ($( + // When checked_shX is added for other shift sizes, uncomment some of these. + // impl_sh! { $t, u8 } + // impl_sh! { $t, u16 } + impl_sh! { $t, u32 } + //impl_sh! { $t, u64 } + //impl_sh! { $t, usize } + + //impl_sh! { $t, i8 } + //impl_sh! { $t, i16 } + //impl_sh! { $t, i32 } + //impl_sh! { $t, i64 } + //impl_sh! { $t, isize } + + // impl_sh_reverse! { u8, $t } + // impl_sh_reverse! { u16, $t } + impl_sh_reverse! { u32, $t } + //impl_sh_reverse! { u64, $t } + //impl_sh_reverse! { usize, $t } + + //impl_sh_reverse! { i8, $t } + //impl_sh_reverse! { i16, $t } + //impl_sh_reverse! { i32, $t } + //impl_sh_reverse! { i64, $t } + //impl_sh_reverse! { isize, $t } + )*) +} + +impl_sh_all! { u8 u16 u32 u64 usize i8 i16 i32 i64 isize } + +// implements unary operators for checked types +macro_rules! impl_unop { + (impl $imp:ident, $method:ident, $checked_method:ident for $t:ty {}) => { + impl $imp for Checked<$t> { + type Output = Checked<$t>; + + fn $method(self) -> Checked<$t> { + match *self { + Some(x) => Checked(x.$checked_method()), + None => Checked(None), + } + } + } + + forward_ref_unop! { impl $imp, $method for Checked<$t> {} } + }; +} + +// implements unary operators for checked types (with no checked method) +macro_rules! impl_unop_unchecked { + (impl $imp:ident, $method:ident for $t:ty {$op:tt}) => { + impl $imp for Checked<$t> { + type Output = Checked<$t>; + + fn $method(self) -> Checked<$t> { + match *self { + Some(x) => Checked(Some($op x)), + None => Checked(None) + } + } + } + + forward_ref_unop! { impl $imp, $method for Checked<$t> {} } + } +} + +// implements binary operators for checked types +macro_rules! impl_binop { + (impl $imp:ident, $method:ident, $checked_method:ident for $t:ty {}) => { + impl $imp for Checked<$t> { + type Output = Checked<$t>; + + fn $method(self, other: Checked<$t>) -> Checked<$t> { + match (*self, *other) { + (Some(x), Some(y)) => Checked(x.$checked_method(y)), + _ => Checked(None), + } + } + } + + impl $imp<$t> for Checked<$t> { + type Output = Checked<$t>; + + fn $method(self, other: $t) -> Checked<$t> { + match *self { + Some(x) => Checked(x.$checked_method(other)), + _ => Checked(None), + } + } + } + + impl $imp<Checked<$t>> for $t { + type Output = Checked<$t>; + + fn $method(self, other: Checked<$t>) -> Checked<$t> { + match *other { + Some(x) => Checked(self.$checked_method(x)), + None => Checked(None), + } + } + } + + forward_ref_binop! { impl $imp, $method for Checked<$t>, Checked<$t> {} } + forward_ref_binop! { impl $imp, $method for Checked<$t>, $t {} } + forward_ref_binop! { impl $imp, $method for $t, Checked<$t> {} } + }; +} + +// implements binary operators for checked types (no checked method) +macro_rules! impl_binop_unchecked { + (impl $imp:ident, $method:ident for $t:ty {$op:tt}) => { + impl $imp for Checked<$t> { + type Output = Checked<$t>; + + fn $method(self, other: Checked<$t>) -> Checked<$t> { + match (*self, *other) { + (Some(x), Some(y)) => Checked(Some(x $op y)), + _ => Checked(None), + } + } + } + + impl $imp<$t> for Checked<$t> { + type Output = Checked<$t>; + + fn $method(self, other: $t) -> Checked<$t> { + match *self { + Some(x) => Checked(Some(x $op other)), + _ => Checked(None), + } + } + } + + impl $imp<Checked<$t>> for $t { + type Output = Checked<$t>; + + fn $method(self, other: Checked<$t>) -> Checked<$t> { + match *other { + Some(x) => Checked(Some(self $op x)), + None => Checked(None), + } + } + } + + forward_ref_binop! { impl $imp, $method for Checked<$t>, Checked<$t> {} } + forward_ref_binop! { impl $imp, $method for Checked<$t>, $t {} } + forward_ref_binop! { impl $imp, $method for $t, Checked<$t> {} } + } +} + +// implements assignment operators for checked types +macro_rules! impl_binop_assign { + (impl $imp:ident, $method:ident for $t:ty {$op:tt}) => { + impl $imp for Checked<$t> { + #[inline] + fn $method(&mut self, other: Checked<$t>) { + *self = *self $op other; + } + } + + impl $imp<$t> for Checked<$t> { + #[inline] + fn $method(&mut self, other: $t) { + *self = *self $op other; + } + } + }; +} + +macro_rules! checked_impl { + ($($t:ty)*) => { + $( + impl_binop! { impl Add, add, checked_add for $t {} } + impl_binop_assign! { impl AddAssign, add_assign for $t {+} } + impl_binop! { impl Sub, sub, checked_sub for $t {} } + impl_binop_assign! { impl SubAssign, sub_assign for $t {-} } + impl_binop! { impl Mul, mul, checked_mul for $t {} } + impl_binop_assign! { impl MulAssign, mul_assign for $t {*} } + impl_binop! { impl Div, div, checked_div for $t {} } + impl_binop_assign! { impl DivAssign, div_assign for $t {/} } + impl_binop! { impl Rem, rem, checked_rem for $t {} } + impl_binop_assign! { impl RemAssign, rem_assign for $t {%} } + impl_unop_unchecked! { impl Not, not for $t {!} } + impl_binop_unchecked! { impl BitXor, bitxor for $t {^} } + impl_binop_assign! { impl BitXorAssign, bitxor_assign for $t {^} } + impl_binop_unchecked! { impl BitOr, bitor for $t {|} } + impl_binop_assign! { impl BitOrAssign, bitor_assign for $t {|} } + impl_binop_unchecked! { impl BitAnd, bitand for $t {&} } + impl_binop_assign! { impl BitAndAssign, bitand_assign for $t {&} } + impl_unop! { impl Neg, neg, checked_neg for $t {} } + + )* + }; +} + +checked_impl! { u8 u16 u32 u64 usize i8 i16 i32 i64 isize }
src/client/auth/sasl.rs+2 −2 modified@@ -42,7 +42,7 @@ impl SaslStart { body.insert("options", doc! { "skipEmptyExchange": true }); } - let mut command = Command::new("saslStart".into(), self.source, body); + let mut command = Command::new("saslStart", self.source, body); if let Some(server_api) = self.server_api { command.set_server_api(&server_api); } @@ -81,7 +81,7 @@ impl SaslContinue { "payload": Binary { subtype: BinarySubtype::Generic, bytes: self.payload }, }; - let mut command = Command::new("saslContinue".into(), self.source, body); + let mut command = Command::new("saslContinue", self.source, body); if let Some(server_api) = self.server_api { command.set_server_api(&server_api); }
src/client/auth/x509.rs+1 −1 modified@@ -25,7 +25,7 @@ pub(crate) fn build_client_first( auth_command_doc.insert("username", username); } - let mut command = Command::new("authenticate".into(), "$external".into(), auth_command_doc); + let mut command = Command::new("authenticate", "$external", auth_command_doc); if let Some(server_api) = server_api { command.set_server_api(server_api); }
src/client/executor.rs+18 −24 modified@@ -23,10 +23,12 @@ use crate::{ WatchArgs, }, cmap::{ - conn::PinnedConnectionHandle, + conn::{ + wire::{next_request_id, Message}, + PinnedConnectionHandle, + }, Connection, ConnectionPool, - RawCommand, RawCommandResponse, }, cursor::{session::SessionCursor, Cursor, CursorSpecification}, @@ -573,51 +575,43 @@ impl Client { let connection_info = connection.info(); let service_id = connection.service_id(); - let request_id = crate::cmap::conn::next_request_id(); + let request_id = next_request_id(); if let Some(ref server_api) = self.inner.options.server_api { cmd.set_server_api(server_api); } let should_redact = cmd.should_redact(); + let should_compress = cmd.should_compress(); let cmd_name = cmd.name.clone(); let target_db = cmd.target_db.clone(); - let serialized = op.serialize_command(cmd)?; + #[allow(unused_mut)] + let mut message = Message::from_command(cmd, Some(request_id))?; #[cfg(feature = "in-use-encryption-unstable")] - let serialized = { + { let guard = self.inner.csfle.read().await; if let Some(ref csfle) = *guard { if csfle.opts().bypass_auto_encryption != Some(true) { - self.auto_encrypt(csfle, RawDocument::from_bytes(&serialized)?, &target_db) - .await? - .into_bytes() - } else { - serialized + let encrypted_payload = self + .auto_encrypt(csfle, &message.document_payload, &target_db) + .await?; + message.document_payload = encrypted_payload; } - } else { - serialized } - }; - let raw_cmd = RawCommand { - name: cmd_name.clone(), - target_db, - exhaust_allowed: false, - bytes: serialized, - }; + } self.emit_command_event(|| { let command_body = if should_redact { Document::new() } else { - Document::from_reader(raw_cmd.bytes.as_slice()) - .unwrap_or_else(|e| doc! { "serialization error": e.to_string() }) + message.get_command_document() }; CommandEvent::Started(CommandStartedEvent { command: command_body, - db: raw_cmd.target_db.clone(), - command_name: raw_cmd.name.clone(), + db: target_db.clone(), + command_name: cmd_name.clone(), request_id, connection: connection_info.clone(), service_id, @@ -626,7 +620,7 @@ impl Client { .await; let start_time = Instant::now(); - let command_result = match connection.send_raw_command(raw_cmd, request_id).await { + let command_result = match connection.send_message(message, should_compress).await { Ok(response) => { async fn handle_response<T: Operation>( client: &Client,
src/client/options.rs+5 −1 modified@@ -738,7 +738,11 @@ impl Serialize for ClientOptions { writeconcern: &self.write_concern, loadbalanced: &self.load_balanced, zlibcompressionlevel: &None, - srvmaxhosts: self.srv_max_hosts.map(|v| v as i32), + srvmaxhosts: self + .srv_max_hosts + .map(|v| v.try_into()) + .transpose() + .map_err(serde::ser::Error::custom)?, }; client_options.serialize(serializer)
src/cmap/conn/command.rs+32 −42 modified@@ -1,11 +1,10 @@ use bson::{RawDocument, RawDocumentBuf}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use super::wire::Message; +use super::wire::{message::DocumentSequence, Message}; use crate::{ - bson::{rawdoc, Document}, - bson_util::extend_raw_document_buf, - client::{options::ServerApi, ClusterTime, HELLO_COMMAND_NAMES, REDACTED_COMMANDS}, + bson::Document, + client::{options::ServerApi, ClusterTime}, error::{Error, ErrorKind, Result}, hello::{HelloCommandResponse, HelloReply}, operation::{CommandErrorBody, CommandResponse}, @@ -14,28 +13,14 @@ use crate::{ ClientSession, }; -/// A command that has been serialized to BSON. -#[derive(Debug)] -pub(crate) struct RawCommand { - pub(crate) name: String, - pub(crate) target_db: String, - /// Whether or not the server may respond to this command multiple times via the moreToComeBit. - pub(crate) exhaust_allowed: bool, - pub(crate) bytes: Vec<u8>, -} - -impl RawCommand { - pub(crate) fn should_compress(&self) -> bool { - let name = self.name.to_lowercase(); - !REDACTED_COMMANDS.contains(name.as_str()) && !HELLO_COMMAND_NAMES.contains(name.as_str()) - } -} - /// Driver-side model of a database command. #[serde_with::skip_serializing_none] #[derive(Clone, Debug, Serialize, Default)] #[serde(rename_all = "camelCase")] -pub(crate) struct Command<T = Document> { +pub(crate) struct Command<T = Document> +where + T: Serialize, +{ #[serde(skip)] pub(crate) name: String, @@ -45,6 +30,9 @@ pub(crate) struct Command<T = Document> { #[serde(flatten)] pub(crate) body: T, + #[serde(skip)] + pub(crate) document_sequences: Vec<DocumentSequence>, + #[serde(rename = "$db")] pub(crate) target_db: String, @@ -70,13 +58,17 @@ pub(crate) struct Command<T = Document> { recovery_token: Option<Document>, } -impl<T> Command<T> { - pub(crate) fn new(name: String, target_db: String, body: T) -> Self { +impl<T> Command<T> +where + T: Serialize, +{ + pub(crate) fn new(name: impl ToString, target_db: impl ToString, body: T) -> Self { Self { - name, - target_db, + name: name.to_string(), + target_db: target_db.to_string(), exhaust_allowed: false, body, + document_sequences: Vec::new(), lsid: None, cluster_time: None, server_api: None, @@ -100,6 +92,7 @@ impl<T> Command<T> { target_db, exhaust_allowed: false, body, + document_sequences: Vec::new(), lsid: None, cluster_time: None, server_api: None, @@ -112,6 +105,17 @@ impl<T> Command<T> { } } + pub(crate) fn add_document_sequence( + &mut self, + identifier: impl ToString, + documents: Vec<RawDocumentBuf>, + ) { + self.document_sequences.push(DocumentSequence { + identifier: identifier.to_string(), + documents, + }); + } + pub(crate) fn set_session(&mut self, session: &ClientSession) { self.lsid = Some(session.id().clone()) } @@ -178,19 +182,6 @@ impl<T> Command<T> { } } -impl Command<RawDocumentBuf> { - pub(crate) fn into_bson_bytes(mut self) -> Result<Vec<u8>> { - let mut command = self.body; - - // Clear the body of the command to avoid re-serializing. - self.body = rawdoc! {}; - let rest_of_command = bson::to_raw_document_buf(&self)?; - - extend_raw_document_buf(&mut command, rest_of_command)?; - Ok(command.into_bytes()) - } -} - #[derive(Debug, Clone)] pub(crate) struct RawCommandResponse { pub(crate) source: ServerAddress, @@ -220,9 +211,8 @@ impl RawCommandResponse { ) } - pub(crate) fn new(source: ServerAddress, message: Message) -> Result<Self> { - let raw = message.single_document_response()?; - Ok(Self::new_raw(source, RawDocumentBuf::from_bytes(raw)?)) + pub(crate) fn new(source: ServerAddress, message: Message) -> Self { + Self::new_raw(source, message.document_payload) } pub(crate) fn new_raw(source: ServerAddress, raw: RawDocumentBuf) -> Self {
src/cmap/conn.rs+13 −23 modified@@ -1,6 +1,6 @@ mod command; mod stream_description; -mod wire; +pub(crate) mod wire; use std::{ sync::Arc, @@ -33,9 +33,8 @@ use crate::{ options::ServerAddress, runtime::AsyncStream, }; -pub(crate) use command::{Command, RawCommand, RawCommandResponse}; +pub(crate) use command::{Command, RawCommandResponse}; pub(crate) use stream_description::StreamDescription; -pub(crate) use wire::next_request_id; /// User-facing information about a connection to the database. #[derive(Clone, Debug, Serialize)] @@ -181,6 +180,7 @@ impl Connection { ) } + #[allow(clippy::cast_possible_truncation)] pub(crate) fn info(&self) -> ConnectionInfo { ConnectionInfo { id: self.id, @@ -273,7 +273,7 @@ impl Connection { } } - async fn send_message( + pub(crate) async fn send_message( &mut self, message: Message, to_compress: bool, @@ -318,7 +318,10 @@ impl Connection { let response_message = response_message_result?; self.more_to_come = response_message.flags.contains(MessageFlags::MORE_TO_COME); - RawCommandResponse::new(self.address.clone(), response_message) + Ok(RawCommandResponse::new( + self.address.clone(), + response_message, + )) } /// Executes a `Command` and returns a `CommandResponse` containing the result from the server. @@ -332,23 +335,7 @@ impl Connection { request_id: impl Into<Option<i32>>, ) -> Result<RawCommandResponse> { let to_compress = command.should_compress(); - let message = Message::with_command(command, request_id.into())?; - self.send_message(message, to_compress).await - } - - /// Executes a `RawCommand` and returns a `CommandResponse` containing the result from the - /// server. - /// - /// An `Ok(...)` result simply means the server received the command and that the driver - /// received the response; it does not imply anything about the success of the command - /// itself. - pub(crate) async fn send_raw_command( - &mut self, - command: RawCommand, - request_id: impl Into<Option<i32>>, - ) -> Result<RawCommandResponse> { - let to_compress = command.should_compress(); - let message = Message::with_raw_command(command, request_id.into()); + let message = Message::from_command(command, request_id.into())?; self.send_message(message, to_compress).await } @@ -379,7 +366,10 @@ impl Connection { let response_message = response_message_result?; self.more_to_come = response_message.flags.contains(MessageFlags::MORE_TO_COME); - RawCommandResponse::new(self.address.clone(), response_message) + Ok(RawCommandResponse::new( + self.address.clone(), + response_message, + )) } /// Gets the connection's StreamDescription.
src/cmap/conn/stream_description.rs+1 −1 modified@@ -99,7 +99,7 @@ impl StreamDescription { max_bson_object_size: 16 * 1024 * 1024, max_write_batch_size: 100_000, hello_ok: false, - max_message_size_bytes: Default::default(), + max_message_size_bytes: 48_000_000, service_id: None, } }
src/cmap/conn/wire/message.rs+164 −149 modified@@ -1,85 +1,87 @@ use std::io::Read; use bitflags::bitflags; +use bson::{doc, Array, Document}; +use serde::Serialize; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; -use super::header::{Header, OpCode}; +use super::{ + header::{Header, OpCode}, + next_request_id, +}; use crate::{ + bson::RawDocumentBuf, bson_util, - cmap::{ - conn::{command::RawCommand, wire::util::SyncCountReader}, - Command, - }, + checked::Checked, + cmap::{conn::wire::util::SyncCountReader, Command}, + compression::{Compressor, Decoder}, error::{Error, ErrorKind, Result}, runtime::SyncLittleEndianRead, }; -use crate::compression::{Compressor, Decoder}; - /// Represents an OP_MSG wire protocol operation. #[derive(Debug)] pub(crate) struct Message { + // OP_MSG payload type 0 + pub(crate) document_payload: RawDocumentBuf, + // OP_MSG payload type 1 + pub(crate) document_sequences: Vec<DocumentSequence>, pub(crate) response_to: i32, pub(crate) flags: MessageFlags, - pub(crate) sections: Vec<MessageSection>, pub(crate) checksum: Option<u32>, pub(crate) request_id: Option<i32>, } +#[derive(Clone, Debug)] +pub(crate) struct DocumentSequence { + pub(crate) identifier: String, + pub(crate) documents: Vec<RawDocumentBuf>, +} + impl Message { - /// Creates a `Message` from a given `Command`. - /// - /// Note that `response_to` will need to be set manually. - pub(crate) fn with_command(command: Command, request_id: Option<i32>) -> Result<Self> { - let bytes = bson::to_vec(&command)?; - Ok(Self::with_raw_command( - RawCommand { - bytes, - target_db: command.target_db, - name: command.name, - exhaust_allowed: command.exhaust_allowed, - }, - request_id, - )) - } + /// Creates a `Message` from a given `Command`. Note that the `response_to` field must be set + /// manually. + pub(crate) fn from_command<T: Serialize>( + command: Command<T>, + request_id: Option<i32>, + ) -> Result<Self> { + let document_payload = bson::to_raw_document_buf(&command)?; - /// Creates a `Message` from a given `Command`. - /// - /// Note that `response_to` will need to be set manually. - pub(crate) fn with_raw_command(command: RawCommand, request_id: Option<i32>) -> Self { let mut flags = MessageFlags::empty(); if command.exhaust_allowed { flags |= MessageFlags::EXHAUST_ALLOWED; } - Self { + Ok(Self { + document_payload, + document_sequences: command.document_sequences, response_to: 0, flags, - sections: vec![MessageSection::Document(command.bytes)], checksum: None, request_id, - } + }) } - /// Gets the first document contained in this Message. - pub(crate) fn single_document_response(self) -> Result<Vec<u8>> { - let section = self.sections.into_iter().next().ok_or_else(|| { - Error::new( - ErrorKind::InvalidResponse { - message: "no response received from server".into(), - }, - Option::<Vec<String>>::None, - ) - })?; - match section { - MessageSection::Document(doc) => Some(doc), - MessageSection::Sequence { documents, .. } => documents.into_iter().next(), + /// Gets this message's command as a Document. If serialization fails, returns a document + /// containing the error. + pub(crate) fn get_command_document(&self) -> Document { + let mut command = match self.document_payload.to_document() { + Ok(document) => document, + Err(error) => return doc! { "serialization error": error.to_string() }, + }; + + for document_sequence in &self.document_sequences { + let mut documents = Array::new(); + for document in &document_sequence.documents { + match document.to_document() { + Ok(document) => documents.push(document.into()), + Err(error) => return doc! { "serialization error": error.to_string() }, + } + } + command.insert(document_sequence.identifier.clone(), documents); } - .ok_or_else(|| { - Error::from(ErrorKind::InvalidResponse { - message: "no message received from the server".to_string(), - }) - }) + + command } /// Reads bytes from `reader` and deserializes them into a Message. @@ -120,21 +122,22 @@ impl Message { mut reader: T, header: &Header, ) -> Result<Self> { - // TODO: RUST-616 ensure length is < maxMessageSizeBytes - let length_remaining = header.length - Header::LENGTH as i32; - let mut buf = vec![0u8; length_remaining as usize]; + let length = Checked::<usize>::try_from(header.length)?; + let length_remaining = length - Header::LENGTH; + let mut buf = vec![0u8; length_remaining.get()?]; reader.read_exact(&mut buf).await?; let reader = buf.as_slice(); - Self::read_op_common(reader, length_remaining, header) + Self::read_op_common(reader, length_remaining.get()?, header) } async fn read_from_op_compressed<T: AsyncRead + Unpin + Send>( mut reader: T, header: &Header, ) -> Result<Self> { - let length_remaining = header.length - Header::LENGTH as i32; - let mut buf = vec![0u8; length_remaining as usize]; + let length = Checked::<usize>::try_from(header.length)?; + let length_remaining = length - Header::LENGTH; + let mut buf = vec![0u8; length_remaining.get()?]; reader.read_exact(&mut buf).await?; let mut reader = buf.as_slice(); @@ -152,7 +155,7 @@ impl Message { } // Read uncompressed size - let uncompressed_size = reader.read_i32_sync()?; + let uncompressed_size = Checked::<usize>::try_from(reader.read_i32_sync()?)?; // Read compressor id let compressor_id: u8 = reader.read_u8_sync()?; @@ -164,7 +167,7 @@ impl Message { let decoded_message = decoder.decode(reader)?; // Check that claimed length matches original length - if decoded_message.len() as i32 != uncompressed_size { + if decoded_message.len() != uncompressed_size.get()? { return Err(ErrorKind::InvalidResponse { message: format!( "The server's message claims that the uncompressed length is {}, but was \ @@ -180,78 +183,89 @@ impl Message { let reader = decoded_message.as_slice(); let length_remaining = decoded_message.len(); - Self::read_op_common(reader, length_remaining as i32, header) + Self::read_op_common(reader, length_remaining, header) } - fn read_op_common( - mut reader: &[u8], - mut length_remaining: i32, - header: &Header, - ) -> Result<Self> { + fn read_op_common(mut reader: &[u8], length_remaining: usize, header: &Header) -> Result<Self> { + let mut length_remaining = Checked::new(length_remaining); let flags = MessageFlags::from_bits_truncate(reader.read_u32_sync()?); - length_remaining -= std::mem::size_of::<u32>() as i32; + length_remaining -= std::mem::size_of::<u32>(); let mut count_reader = SyncCountReader::new(&mut reader); - let mut sections = Vec::new(); - - while length_remaining - count_reader.bytes_read() as i32 > 4 { - sections.push(MessageSection::read(&mut count_reader)?); + let mut document_payload = None; + let mut document_sequences = Vec::new(); + while (length_remaining - count_reader.bytes_read()).get()? > 4 { + let next_section = MessageSection::read(&mut count_reader)?; + match next_section { + MessageSection::Document(document) => { + if document_payload.is_some() { + return Err(ErrorKind::InvalidResponse { + message: "an OP_MSG response must contain exactly one payload type 0 \ + section" + .into(), + } + .into()); + } else { + document_payload = Some(document); + } + } + MessageSection::Sequence(document_sequence) => { + document_sequences.push(document_sequence) + } + } } - length_remaining -= count_reader.bytes_read() as i32; + length_remaining -= count_reader.bytes_read(); let mut checksum = None; - if length_remaining == 4 && flags.contains(MessageFlags::CHECKSUM_PRESENT) { + if length_remaining.get()? == 4 && flags.contains(MessageFlags::CHECKSUM_PRESENT) { checksum = Some(reader.read_u32_sync()?); - } else if length_remaining != 0 { - return Err(ErrorKind::InvalidResponse { - message: format!( - "The server indicated that the reply would be {} bytes long, but it instead \ - was {}", - header.length, - header.length - length_remaining + count_reader.bytes_read() as i32, - ), - } - .into()); + } else if length_remaining.get()? != 0 { + let header_len = Checked::<usize>::try_from(header.length)?; + return Err(Error::invalid_response(format!( + "The server indicated that the reply would be {} bytes long, but it instead was {}", + header.length, + header_len - length_remaining + count_reader.bytes_read(), + ))); } Ok(Self { response_to: header.response_to, flags, - sections, + document_payload: document_payload.ok_or_else(|| ErrorKind::InvalidResponse { + message: "an OP_MSG response must contain exactly one payload type 0 section" + .into(), + })?, + document_sequences, checksum, request_id: None, }) } /// Serializes the Message to bytes and writes them to `writer`. pub(crate) async fn write_to<T: AsyncWrite + Send + Unpin>(&self, mut writer: T) -> Result<()> { - let mut sections_bytes = Vec::new(); + let sections = self.get_sections_bytes()?; - for section in &self.sections { - section.write(&mut sections_bytes).await?; - } - - let total_length = Header::LENGTH + let total_length = Checked::new(Header::LENGTH) + std::mem::size_of::<u32>() - + sections_bytes.len() + + sections.len() + self .checksum .as_ref() .map(std::mem::size_of_val) .unwrap_or(0); let header = Header { - length: total_length as i32, - request_id: self.request_id.unwrap_or_else(super::util::next_request_id), + length: total_length.try_into()?, + request_id: self.request_id.unwrap_or_else(next_request_id), response_to: self.response_to, op_code: OpCode::Message, }; header.write_to(&mut writer).await?; writer.write_u32_le(self.flags.bits()).await?; - writer.write_all(§ions_bytes).await?; + writer.write_all(§ions).await?; if let Some(checksum) = self.checksum { writer.write_u32_le(checksum).await?; @@ -263,36 +277,33 @@ impl Message { } /// Serializes message to bytes, compresses those bytes, and writes the bytes. - pub async fn write_compressed_to<T: AsyncWrite + Unpin + Send>( + pub(crate) async fn write_compressed_to<T: AsyncWrite + Unpin + Send>( &self, mut writer: T, compressor: &Compressor, ) -> Result<()> { let mut encoder = compressor.to_encoder()?; let compressor_id = compressor.id() as u8; - let mut sections_bytes = Vec::new(); + let sections = self.get_sections_bytes()?; - for section in &self.sections { - section.write(&mut sections_bytes).await?; - } let flag_bytes = &self.flags.bits().to_le_bytes(); - let uncompressed_len = sections_bytes.len() + flag_bytes.len(); + let uncompressed_len = Checked::new(sections.len()) + flag_bytes.len(); // Compress the flags and sections. Depending on the handshake // this could use zlib, zstd or snappy encoder.write_all(flag_bytes)?; - encoder.write_all(sections_bytes.as_slice())?; + encoder.write_all(sections.as_slice())?; let compressed_bytes = encoder.finish()?; - let total_length = Header::LENGTH + let total_length = Checked::new(Header::LENGTH) + std::mem::size_of::<i32>() + std::mem::size_of::<i32>() + std::mem::size_of::<u8>() + compressed_bytes.len(); let header = Header { - length: total_length as i32, - request_id: self.request_id.unwrap_or_else(super::util::next_request_id), + length: total_length.try_into()?, + request_id: self.request_id.unwrap_or_else(next_request_id), response_to: self.response_to, op_code: OpCode::Compressed, }; @@ -302,7 +313,7 @@ impl Message { // Write original (pre-compressed) opcode (always OP_MSG) writer.write_i32_le(OpCode::Message as i32).await?; // Write uncompressed size - writer.write_i32_le(uncompressed_len as i32).await?; + writer.write_i32_le(uncompressed_len.try_into()?).await?; // Write compressor id writer.write_u8(compressor_id).await?; // Write compressed message @@ -312,6 +323,41 @@ impl Message { Ok(()) } + + fn get_sections_bytes(&self) -> Result<Vec<u8>> { + let mut sections = Vec::new(); + + // Payload type 0 + sections.push(0); + sections.extend(self.document_payload.as_bytes()); + + for document_sequence in &self.document_sequences { + // Payload type 1 + sections.push(1); + + let identifier_bytes = document_sequence.identifier.as_bytes(); + + let documents_size = document_sequence + .documents + .iter() + .fold(0, |running_size, document| { + running_size + document.as_bytes().len() + }); + + // Size bytes + identifier bytes + null-terminator byte + document bytes + let size = Checked::new(4) + identifier_bytes.len() + 1 + documents_size; + sections.extend(size.try_into::<i32>()?.to_le_bytes()); + + sections.extend(identifier_bytes); + sections.push(0); + + for document in &document_sequence.documents { + sections.extend(document.as_bytes()); + } + } + + Ok(sections) + } } const DEFAULT_MAX_MESSAGE_SIZE_BYTES: i32 = 48 * 1024 * 1024; @@ -327,13 +373,9 @@ bitflags! { /// Represents a section as defined by the OP_MSG spec. #[derive(Debug)] -pub(crate) enum MessageSection { - Document(Vec<u8>), - Sequence { - size: i32, - identifier: String, - documents: Vec<Vec<u8>>, - }, +enum MessageSection { + Document(RawDocumentBuf), + Sequence(DocumentSequence), } impl MessageSection { @@ -342,68 +384,41 @@ impl MessageSection { let payload_type = reader.read_u8_sync()?; if payload_type == 0 { - return Ok(MessageSection::Document(bson_util::read_document_bytes( - reader, - )?)); + let bytes = bson_util::read_document_bytes(reader)?; + let document = RawDocumentBuf::from_bytes(bytes)?; + return Ok(MessageSection::Document(document)); } - let size = reader.read_i32_sync()?; - let mut length_remaining = size - std::mem::size_of::<i32>() as i32; + let size = Checked::<usize>::try_from(reader.read_i32_sync()?)?; + let mut length_remaining = size - std::mem::size_of::<i32>(); let mut identifier = String::new(); - length_remaining -= reader.read_to_string(&mut identifier)? as i32; + length_remaining -= reader.read_to_string(&mut identifier)?; let mut documents = Vec::new(); let mut count_reader = SyncCountReader::new(reader); - while length_remaining > count_reader.bytes_read() as i32 { - documents.push(bson_util::read_document_bytes(&mut count_reader)?); + while length_remaining.get()? > count_reader.bytes_read() { + let bytes = bson_util::read_document_bytes(&mut count_reader)?; + let document = RawDocumentBuf::from_bytes(bytes)?; + documents.push(document); } - if length_remaining != count_reader.bytes_read() as i32 { + if length_remaining.get()? != count_reader.bytes_read() { return Err(ErrorKind::InvalidResponse { message: format!( "The server indicated that the reply would be {} bytes long, but it instead \ was {}", size, - length_remaining + count_reader.bytes_read() as i32, + length_remaining + count_reader.bytes_read(), ), } .into()); } - Ok(MessageSection::Sequence { - size, + Ok(MessageSection::Sequence(DocumentSequence { identifier, documents, - }) - } - - /// Serializes the MessageSection to bytes and writes them to `writer`. - async fn write<W: AsyncWrite + Unpin + Send>(&self, writer: &mut W) -> Result<()> { - match self { - Self::Document(doc) => { - // Write payload type. - writer.write_u8(0).await?; - writer.write_all(doc.as_slice()).await?; - } - Self::Sequence { - size, - identifier, - documents, - } => { - // Write payload type. - writer.write_u8(1).await?; - - writer.write_i32_le(*size).await?; - super::util::write_cstring(writer, identifier).await?; - - for doc in documents { - writer.write_all(doc.as_slice()).await?; - } - } - } - - Ok(()) + })) } }
src/cmap/conn/wire.rs+1 −1 modified@@ -1,5 +1,5 @@ mod header; -mod message; +pub(crate) mod message; mod util; pub(crate) use self::{
src/cmap/conn/wire/util.rs+0 −17 modified@@ -4,9 +4,6 @@ use std::{ }; use lazy_static::lazy_static; -use tokio::io::{AsyncWrite, AsyncWriteExt}; - -use crate::error::Result; /// Closure to obtain a new, unique request ID. pub(crate) fn next_request_id() -> i32 { @@ -17,20 +14,6 @@ pub(crate) fn next_request_id() -> i32 { REQUEST_ID.fetch_add(1, Ordering::SeqCst) } -/// Serializes `string` to bytes and writes them to `writer` with a null terminator appended. -pub(super) async fn write_cstring<W: AsyncWrite + Unpin>( - writer: &mut W, - string: &str, -) -> Result<()> { - // Write the string's UTF-8 bytes. - writer.write_all(string.as_bytes()).await?; - - // Write the null terminator. - writer.write_all(&[0]).await?; - - Ok(()) -} - pub(super) struct SyncCountReader<R> { reader: R, bytes_read: usize,
src/cmap.rs+1 −1 modified@@ -15,7 +15,7 @@ use derivative::Derivative; pub use self::conn::ConnectionInfo; pub(crate) use self::{ - conn::{Command, Connection, RawCommand, RawCommandResponse, StreamDescription}, + conn::{Command, Connection, RawCommandResponse, StreamDescription}, status::PoolGenerationSubscriber, worker::PoolGeneration, };
src/collation.rs+1 −1 modified@@ -128,7 +128,7 @@ impl Serialize for CollationStrength { S: serde::Serializer, { let level = u32::from(*self); - serializer.serialize_i32(level as i32) + serializer.serialize_i32(level.try_into().map_err(serde::ser::Error::custom)?) } }
src/coll/options.rs+2 −2 modified@@ -10,7 +10,7 @@ use crate::{ error::Result, options::Collation, selection_criteria::SelectionCriteria, - serde_util, + serde_util::{self, write_concern_is_empty}, }; // Generated code for `Deserialize` or `TypedBuilder` causes a deprecation warning; annotating the @@ -151,7 +151,7 @@ pub struct InsertManyOptions { pub ordered: Option<bool>, /// The write concern for the operation. - #[serde(skip_deserializing)] + #[serde(skip_deserializing, skip_serializing_if = "write_concern_is_empty")] pub write_concern: Option<WriteConcern>, /// Tags the query with an arbitrary [`Bson`] value to help trace the operation through the
src/coll.rs+7 −1 modified@@ -1231,7 +1231,7 @@ where while n_attempted < ds.len() { let docs: Vec<&T> = ds.iter().skip(n_attempted).map(Borrow::borrow).collect(); - let insert = Insert::new_encrypted( + let insert = Insert::new( self.namespace(), docs, options.clone(), @@ -1360,10 +1360,16 @@ where let mut options = options.into(); resolve_write_concern_with_session!(self, options, session.as_ref())?; + #[cfg(feature = "in-use-encryption-unstable")] + let encrypted = self.client().auto_encryption_opts().await.is_some(); + #[cfg(not(feature = "in-use-encryption-unstable"))] + let encrypted = false; + let insert = Insert::new( self.namespace(), vec![doc], options.map(InsertManyOptions::from_insert_one_options), + encrypted, self.inner.human_readable_serialization, ); self.client()
src/error.rs+7 −0 modified@@ -121,6 +121,13 @@ impl Error { .into() } + pub(crate) fn invalid_response(message: impl Into<String>) -> Error { + ErrorKind::InvalidResponse { + message: message.into(), + } + .into() + } + /// Construct a generic network timeout error. pub(crate) fn network_timeout() -> Error { ErrorKind::Io(Arc::new(std::io::ErrorKind::TimedOut.into())).into()
src/gridfs.rs+4 −3 modified@@ -11,6 +11,7 @@ use serde_with::skip_serializing_none; use crate::{ bson::{doc, oid::ObjectId, Bson, DateTime, Document, RawBinaryRef}, + checked::Checked, cursor::Cursor, error::{Error, ErrorKind, GridFsErrorKind, GridFsFileIdentifier, Result}, options::{CollectionOptions, FindOptions, ReadConcern, SelectionCriteria, WriteConcern}, @@ -76,8 +77,8 @@ impl FilesCollectionDocument { fn n_from_vals(length: u64, chunk_size_bytes: u32) -> u32 { let chunk_size_bytes = chunk_size_bytes as u64; - let n = length / chunk_size_bytes + u64::from(length % chunk_size_bytes != 0); - n as u32 + let n = Checked::new(length) / chunk_size_bytes + u64::from(length % chunk_size_bytes != 0); + n.try_into().unwrap() } /// Returns the expected length of a chunk given its index. @@ -88,7 +89,7 @@ impl FilesCollectionDocument { fn expected_chunk_length_from_vals(length: u64, chunk_size_bytes: u32, n: u32) -> u32 { let remainder = length % (chunk_size_bytes as u64); if n == Self::n_from_vals(length, chunk_size_bytes) - 1 && remainder != 0 { - remainder as u32 + Checked::new(remainder).try_into().unwrap() } else { chunk_size_bytes }
src/gridfs/upload.rs+8 −6 modified@@ -14,6 +14,7 @@ use super::{options::GridFsUploadOptions, Chunk, FilesCollectionDocument, GridFs use crate::{ bson::{doc, oid::ObjectId, spec::BinarySubtype, Bson, DateTime, Document, RawBinaryRef}, bson_util::get_int, + checked::Checked, client::AsyncDropToken, error::{Error, ErrorKind, GridFsErrorKind, Result}, index::IndexModel, @@ -501,21 +502,22 @@ async fn write_bytes( chunk_size_bytes: u32, files_id: Bson, ) -> Result<(u32, Vec<u8>)> { + let chunk_size_bytes: usize = Checked::new(chunk_size_bytes).try_into()?; bucket.create_indexes().await?; - let mut n = 0; + let mut n = Checked::new(0); let mut chunks = vec![]; - while buffer.len() as u32 - (n * chunk_size_bytes) >= chunk_size_bytes { + while (Checked::new(buffer.len()) - (n * chunk_size_bytes)).get()? >= chunk_size_bytes { let start = n * chunk_size_bytes; let end = (n + 1) * chunk_size_bytes; let chunk = Chunk { id: ObjectId::new(), files_id: files_id.clone(), - n: starting_n + n, + n: starting_n + n.try_into::<u32>()?, data: RawBinaryRef { subtype: BinarySubtype::Generic, - bytes: &buffer[(start as usize)..(end as usize)], + bytes: &buffer[start.get()?..end.get()?], }, }; n += 1; @@ -524,8 +526,8 @@ async fn write_bytes( match bucket.chunks().insert_many(chunks, None).await { Ok(_) => { - buffer.drain(..(n * chunk_size_bytes) as usize); - Ok((n, buffer)) + buffer.drain(..(n * chunk_size_bytes).get()?); + Ok((n.try_into()?, buffer)) } Err(error) => match clean_up_chunks(files_id, bucket.chunks().clone(), Some(error)).await { // clean_up_chunks will always return an error if one is passed in, so this case is
src/hello.rs+9 −2 modified@@ -54,10 +54,16 @@ pub(crate) fn hello_command( if let Some(opts) = awaitable_options { command.insert("topologyVersion", opts.topology_version); - command.insert("maxAwaitTimeMS", opts.max_await_time.as_millis() as i64); + command.insert( + "maxAwaitTimeMS", + opts.max_await_time + .as_millis() + .try_into() + .unwrap_or(i64::MAX), + ); } - let mut command = Command::new(command_name.into(), "admin".into(), command); + let mut command = Command::new(command_name, "admin", command); if let Some(server_api) = server_api { command.set_server_api(server_api); } @@ -183,6 +189,7 @@ pub(crate) struct HelloCommandResponse { pub connection_id: Option<i64>, } +#[allow(clippy::cast_possible_truncation)] fn deserialize_connection_id<'de, D: serde::Deserializer<'de>>( de: D, ) -> std::result::Result<Option<i64>, D::Error> {
src/lib.rs+3 −0 modified@@ -294,6 +294,8 @@ #![warn(missing_docs)] #![warn(rustdoc::missing_crate_level_docs)] +#![warn(clippy::cast_possible_truncation)] +#![warn(clippy::cast_possible_wrap)] #![cfg_attr( feature = "cargo-clippy", allow( @@ -320,6 +322,7 @@ pub use ::mongocrypt; mod bson_util; pub mod change_stream; +pub(crate) mod checked; mod client; mod cmap; mod coll;
src/operation/aggregate/test.rs+2 −161 modified@@ -1,175 +1,16 @@ use std::time::Duration; -use super::AggregateTarget; use crate::{ - bson::{doc, Document}, - bson_util, - cmap::StreamDescription, - concern::{ReadConcern, ReadConcernLevel}, + bson::doc, error::{ErrorKind, WriteFailure}, operation::{ test::{self, handle_response_test}, Aggregate, - Operation, }, - options::{AggregateOptions, Hint}, + options::AggregateOptions, Namespace, }; -fn build_test( - target: impl Into<AggregateTarget>, - pipeline: Vec<Document>, - options: Option<AggregateOptions>, - mut expected_body: Document, -) { - let target = target.into(); - - let mut aggregate = Aggregate::new(target.clone(), pipeline, options); - - let cmd = aggregate.build(&StreamDescription::new_testing()).unwrap(); - - assert_eq!(cmd.name.as_str(), "aggregate"); - assert_eq!(cmd.target_db.as_str(), target.db_name()); - - let cmd_bytes = aggregate.serialize_command(cmd).unwrap(); - let mut cmd_doc = bson::from_slice(&cmd_bytes).unwrap(); - - bson_util::sort_document(&mut expected_body); - bson_util::sort_document(&mut cmd_doc); - - assert_eq!(cmd_doc, expected_body); -} - -#[test] -fn build() { - let ns = Namespace { - db: "test_db".to_string(), - coll: "test_coll".to_string(), - }; - - let pipeline = vec![doc! { "$match": { "x": 3 }}]; - - let options = AggregateOptions::builder() - .hint(Hint::Keys(doc! { "x": 1, "y": 2 })) - .bypass_document_validation(true) - .read_concern(ReadConcern::from(ReadConcernLevel::Available)) - .build(); - - let expected_body = doc! { - "aggregate": "test_coll", - "$db": "test_db", - "pipeline": bson_util::to_bson_array(&pipeline), - "cursor": {}, - "hint": { - "x": 1, - "y": 2, - }, - "bypassDocumentValidation": true, - "readConcern": { - "level": "available" - }, - }; - - build_test(ns, pipeline, Some(options), expected_body); -} - -#[test] -fn build_batch_size() { - let ns = Namespace { - db: "test_db".to_string(), - coll: "test_coll".to_string(), - }; - - let pipeline = Vec::new(); - - let mut expected_body = doc! { - "aggregate": "test_coll", - "$db": "test_db", - "pipeline": [], - "cursor": {}, - }; - - build_test(ns.clone(), pipeline.clone(), None, expected_body.clone()); - - build_test( - ns.clone(), - pipeline.clone(), - Some(AggregateOptions::default()), - expected_body.clone(), - ); - - let batch_size_options = AggregateOptions::builder().batch_size(5).build(); - expected_body.insert("cursor", doc! { "batchSize": 5 }); - build_test( - ns.clone(), - pipeline, - Some(batch_size_options.clone()), - expected_body.clone(), - ); - - let out_pipeline = vec![doc! { "$out": "cat" }]; - expected_body.insert("cursor", Document::new()); - expected_body.insert("pipeline", bson_util::to_bson_array(&out_pipeline)); - build_test( - ns.clone(), - out_pipeline, - Some(batch_size_options.clone()), - expected_body.clone(), - ); - - let merge_pipeline = vec![doc! { - "$merge": { - "into": "out", - } - }]; - expected_body.insert("pipeline", bson_util::to_bson_array(&merge_pipeline)); - build_test(ns, merge_pipeline, Some(batch_size_options), expected_body); -} - -#[test] -fn build_target() { - let pipeline = Vec::new(); - - let ns = Namespace { - db: "test_db".to_string(), - coll: "test_coll".to_string(), - }; - - let expected_body = doc! { - "aggregate": "test_coll", - "$db": "test_db", - "pipeline": [], - "cursor": {}, - }; - build_test(ns.clone(), pipeline.clone(), None, expected_body); - - let expected_body = doc! { - "aggregate": 1, - "$db": "test_db", - "pipeline": [], - "cursor": {} - }; - build_test(ns.db, pipeline, None, expected_body); -} - -#[test] -fn build_max_await_time() { - let options = AggregateOptions::builder() - .max_await_time(Duration::from_millis(5)) - .max_time(Duration::from_millis(10)) - .build(); - - let body = doc! { - "aggregate": 1, - "$db": "test_db", - "cursor": {}, - "maxTimeMS": 10i32, - "pipeline": [] - }; - - build_test("test_db".to_string(), Vec::new(), Some(options), body); -} - #[test] fn op_selection_criteria() { test::op_selection_criteria(|selection_criteria| {
src/operation/count_documents/test.rs+4 −59 modified@@ -1,14 +1,12 @@ +#![allow(clippy::cast_possible_truncation)] +#![allow(clippy::cast_possible_wrap)] + use crate::{ bson::doc, bson_util, cmap::StreamDescription, coll::Namespace, - concern::ReadConcern, - operation::{ - test::{self, handle_response_test}, - Operation, - }, - options::{CountOptions, Hint}, + operation::{test::handle_response_test, Operation}, }; use super::CountDocuments; @@ -40,59 +38,6 @@ fn build() { assert_eq!(count_command.target_db, "test_db"); } -#[test] -fn build_with_options() { - let skip = 2; - let limit = 5; - let options = CountOptions::builder() - .skip(skip) - .limit(limit) - .hint(Hint::Name("_id_1".to_string())) - .read_concern(ReadConcern::available()) - .build(); - let ns = Namespace { - db: "test_db".to_string(), - coll: "test_coll".to_string(), - }; - let mut count_op = CountDocuments::new(ns, None, Some(options)).unwrap(); - let count_command = count_op - .build(&StreamDescription::new_testing()) - .expect("error on build"); - assert_eq!(count_command.target_db, "test_db"); - - let mut expected_body = doc! { - "aggregate": "test_coll", - "$db": "test_db", - "pipeline": [ - { "$match": {} }, - { "$skip": skip as i64 }, - { "$limit": limit as i64 }, - { "$group": { "_id": 1, "n": { "$sum": 1 } } }, - ], - "hint": "_id_1", - "cursor": { }, - "readConcern": { "level": "available" }, - }; - - bson_util::sort_document(&mut expected_body); - let serialized_command = count_op.serialize_command(count_command).unwrap(); - let mut cmd_doc = bson::from_slice(&serialized_command).unwrap(); - bson_util::sort_document(&mut cmd_doc); - - assert_eq!(cmd_doc, expected_body); -} - -#[test] -fn op_selection_criteria() { - test::op_selection_criteria(|selection_criteria| { - let options = CountOptions { - selection_criteria, - ..Default::default() - }; - CountDocuments::new(Namespace::empty(), None, Some(options)).unwrap() - }); -} - #[test] fn handle_success() { let ns = Namespace {
src/operation/count/test.rs+2 −39 modified@@ -1,17 +1,15 @@ -use std::time::Duration; +#![allow(clippy::cast_possible_truncation)] +#![allow(clippy::cast_possible_wrap)] use crate::{ bson::doc, - bson_util, cmap::StreamDescription, coll::{options::EstimatedDocumentCountOptions, Namespace}, - concern::ReadConcern, operation::{ test::{self, handle_response_test}, Count, Operation, }, - options::ReadConcernLevel, }; #[test] @@ -33,41 +31,6 @@ fn build() { assert_eq!(count_command.target_db, "test_db"); } -#[test] -fn build_with_options() { - let read_concern: ReadConcern = ReadConcernLevel::Local.into(); - let max_time = Duration::from_millis(2_u64); - let options: EstimatedDocumentCountOptions = EstimatedDocumentCountOptions::builder() - .max_time(max_time) - .read_concern(read_concern.clone()) - .build(); - let ns = Namespace { - db: "test_db".to_string(), - coll: "test_coll".to_string(), - }; - let mut count_op = Count::new(ns, Some(options)); - let count_command = count_op - .build(&StreamDescription::new_testing()) - .expect("error on build"); - - assert_eq!(count_command.target_db, "test_db"); - - let mut expected_body = doc! { - "count": "test_coll", - "$db": "test_db", - "maxTimeMS": max_time.as_millis() as i32, - "readConcern": doc! {"level": read_concern.level.as_str().to_string() }, - }; - - let cmd_bytes = count_op.serialize_command(count_command).unwrap(); - let mut cmd_doc = bson::from_slice(&cmd_bytes).unwrap(); - - bson_util::sort_document(&mut cmd_doc); - bson_util::sort_document(&mut expected_body); - - assert_eq!(cmd_doc, expected_body); -} - #[test] fn op_selection_criteria() { test::op_selection_criteria(|selection_criteria| {
src/operation/distinct/test.rs+3 −0 modified@@ -1,3 +1,6 @@ +#![allow(clippy::cast_possible_truncation)] +#![allow(clippy::cast_possible_wrap)] + use std::time::Duration; use crate::{
src/operation/find_and_modify.rs+0 −4 modified@@ -138,10 +138,6 @@ impl<'a, R: Serialize, T: DeserializeOwned> OperationWithDefaults for FindAndMod )) } - fn serialize_command(&mut self, cmd: Command<Self::Command>) -> Result<Vec<u8>> { - cmd.into_bson_bytes() - } - fn handle_response( &self, response: RawCommandResponse,
src/operation/find/test.rs+2 −182 modified@@ -1,195 +1,15 @@ use std::time::Duration; use crate::{ - bson::{doc, Document}, - bson_util, - cmap::StreamDescription, + bson::doc, operation::{ test::{self, handle_response_test}, Find, - Operation, }, - options::{CursorType, FindOptions, Hint, ReadConcern, ReadConcernLevel}, + options::{CursorType, FindOptions}, Namespace, }; -fn build_test( - ns: Namespace, - filter: Option<Document>, - options: Option<FindOptions>, - mut expected_body: Document, -) { - let mut find = Find::new(ns.clone(), filter, options); - - let cmd = find.build(&StreamDescription::new_testing()).unwrap(); - - assert_eq!(cmd.name.as_str(), "find"); - assert_eq!(cmd.target_db.as_str(), ns.db.as_str()); - - let cmd_bytes = find.serialize_command(cmd).unwrap(); - let mut cmd_doc = bson::from_slice(&cmd_bytes).unwrap(); - - bson_util::sort_document(&mut expected_body); - bson_util::sort_document(&mut cmd_doc); - - assert_eq!(cmd_doc, expected_body); -} - -#[test] -fn build() { - let ns = Namespace { - db: "test_db".to_string(), - coll: "test_coll".to_string(), - }; - - let filter = doc! { - "x": 2, - "y": { "$gt": 1 }, - }; - - let options = FindOptions::builder() - .hint(Hint::Keys(doc! { "x": 1, "y": 2 })) - .projection(doc! { "x": 0 }) - .allow_partial_results(true) - .read_concern(ReadConcern::from(ReadConcernLevel::Available)) - .build(); - - let expected_body = doc! { - "find": "test_coll", - "$db": "test_db", - "filter": filter.clone(), - "hint": { - "x": 1, - "y": 2, - }, - "projection": { - "x": 0 - }, - "allowPartialResults": true, - "readConcern": { - "level": "available" - } - }; - - build_test(ns, Some(filter), Some(options), expected_body); -} - -#[test] -fn build_cursor_type() { - let ns = Namespace { - db: "test_db".to_string(), - coll: "test_coll".to_string(), - }; - - let non_tailable_options = FindOptions::builder() - .cursor_type(CursorType::NonTailable) - .build(); - - let non_tailable_body = doc! { - "find": "test_coll", - "$db": "test_db", - }; - - build_test( - ns.clone(), - None, - Some(non_tailable_options), - non_tailable_body, - ); - - let tailable_options = FindOptions::builder() - .cursor_type(CursorType::Tailable) - .build(); - - let tailable_body = doc! { - "find": "test_coll", - "tailable": true, - "$db": "test_db", - }; - - build_test(ns.clone(), None, Some(tailable_options), tailable_body); - - let tailable_await_options = FindOptions::builder() - .cursor_type(CursorType::TailableAwait) - .build(); - - let tailable_await_body = doc! { - "find": "test_coll", - "$db": "test_db", - "tailable": true, - "awaitData": true, - }; - - build_test(ns, None, Some(tailable_await_options), tailable_await_body); -} - -#[test] -fn build_max_await_time() { - let ns = Namespace { - db: "test_db".to_string(), - coll: "test_coll".to_string(), - }; - - let options = FindOptions::builder() - .max_await_time(Duration::from_millis(5)) - .max_time(Duration::from_millis(10)) - .build(); - - let body = doc! { - "find": "test_coll", - "$db": "test_db", - "maxTimeMS": 10i32 - }; - - build_test(ns, None, Some(options), body); -} - -#[test] -fn build_limit() { - let ns = Namespace { - db: "test_db".to_string(), - coll: "test_coll".to_string(), - }; - - let positive_options = FindOptions::builder().limit(5).build(); - - let positive_body = doc! { - "find": "test_coll", - "$db": "test_db", - "limit": 5_i64 - }; - - build_test(ns.clone(), None, Some(positive_options), positive_body); - - let negative_options = FindOptions::builder().limit(-5).build(); - - let negative_body = doc! { - "find": "test_coll", - "$db": "test_db", - "limit": 5_i64, - "singleBatch": true - }; - - build_test(ns, None, Some(negative_options), negative_body); -} - -#[test] -fn build_batch_size() { - let options = FindOptions::builder().batch_size(1).build(); - let body = doc! { - "find": "", - "$db": "", - "batchSize": 1 - }; - build_test(Namespace::empty(), None, Some(options), body); - - let options = FindOptions::builder() - .batch_size((std::i32::MAX as u32) + 1) - .build(); - let mut op = Find::new(Namespace::empty(), None, Some(options)); - assert!(op.build(&StreamDescription::new_testing()).is_err()) -} - #[test] fn op_selection_criteria() { test::op_selection_criteria(|selection_criteria| {
src/operation/get_more.rs+4 −1 modified@@ -70,7 +70,10 @@ impl<'conn> OperationWithDefaults for GetMore<'conn> { } if let Some(ref max_time) = self.max_time { - body.insert("maxTimeMS", max_time.as_millis() as i32); + body.insert( + "maxTimeMS", + max_time.as_millis().try_into().unwrap_or(i32::MAX), + ); } if let Some(ref comment) = self.comment {
src/operation/insert.rs+62 −84 modified@@ -7,51 +7,43 @@ use bson::{oid::ObjectId, Bson, RawArrayBuf, RawDocumentBuf}; use serde::Serialize; use crate::{ - bson::doc, + bson::rawdoc, bson_util, + checked::Checked, cmap::{Command, RawCommandResponse, StreamDescription}, error::{BulkWriteFailure, Error, ErrorKind, Result}, - operation::{ - remove_empty_write_concern, - OperationWithDefaults, - Retryability, - WriteResponseBody, - }, + operation::{OperationWithDefaults, Retryability, WriteResponseBody}, options::{InsertManyOptions, WriteConcern}, results::InsertManyResult, serde_util, Namespace, }; -use super::CommandBody; +use super::{COMMAND_OVERHEAD_SIZE, MAX_ENCRYPTED_WRITE_SIZE}; #[derive(Debug)] pub(crate) struct Insert<'a, T> { ns: Namespace, documents: Vec<&'a T>, inserted_ids: Vec<Bson>, - options: Option<InsertManyOptions>, + options: InsertManyOptions, encrypted: bool, human_readable_serialization: bool, } impl<'a, T> Insert<'a, T> { pub(crate) fn new( - ns: Namespace, - documents: Vec<&'a T>, - options: Option<InsertManyOptions>, - human_readable_serialization: bool, - ) -> Self { - Self::new_encrypted(ns, documents, options, false, human_readable_serialization) - } - - pub(crate) fn new_encrypted( ns: Namespace, documents: Vec<&'a T>, options: Option<InsertManyOptions>, encrypted: bool, human_readable_serialization: bool, ) -> Self { + let mut options = options.unwrap_or_default(); + if options.ordered.is_none() { + options.ordered = Some(true); + } + Self { ns, options, @@ -61,30 +53,26 @@ impl<'a, T> Insert<'a, T> { human_readable_serialization, } } - - fn is_ordered(&self) -> bool { - self.options - .as_ref() - .and_then(|o| o.ordered) - .unwrap_or(true) - } } impl<'a, T: Serialize> OperationWithDefaults for Insert<'a, T> { type O = InsertManyResult; - type Command = InsertCommand; + type Command = RawDocumentBuf; const NAME: &'static str = "insert"; - fn build(&mut self, description: &StreamDescription) -> Result<Command<InsertCommand>> { - let mut docs = RawArrayBuf::new(); + fn build(&mut self, description: &StreamDescription) -> Result<Command<Self::Command>> { + let mut docs = Vec::new(); let mut size = 0; - let batch_size_limit = description.max_bson_object_size as u64; + + let max_doc_size = Checked::<usize>::try_from(description.max_bson_object_size)?; + let max_doc_sequence_size = + Checked::<usize>::try_from(description.max_message_size_bytes)? - COMMAND_OVERHEAD_SIZE; for (i, d) in self .documents .iter() - .take(description.max_write_batch_size as usize) + .take(Checked::new(description.max_write_batch_size).try_into()?) .enumerate() { let mut doc = @@ -105,55 +93,63 @@ impl<'a, T: Serialize> OperationWithDefaults for Insert<'a, T> { bytes.splice(4..4, oid_slice.iter().cloned()); // overwrite old length - let new_length = (bytes.len() as i32).to_le_bytes(); + let new_length = Checked::new(bytes.len()).try_into::<i32>()?.to_le_bytes(); bytes[0..4].copy_from_slice(&new_length); doc = RawDocumentBuf::from_bytes(bytes)?; Bson::ObjectId(oid) } }; - let doc_size = bson_util::array_entry_size_bytes(i, doc.as_bytes().len()); - - if self.encrypted && size > 0 && size + doc_size >= 2_097_152 { - break; + let doc_size = doc.as_bytes().len(); + if doc_size > max_doc_size.get()? { + return Err(ErrorKind::InvalidArgument { + message: format!( + "insert document must be within {} bytes, but document provided is {} \ + bytes", + max_doc_size, doc_size + ), + } + .into()); } - if size + doc_size <= batch_size_limit { - if self.inserted_ids.len() <= i { - self.inserted_ids.push(id); + + // From the spec: Drivers MUST not reduce the size limits for a single write before + // automatic encryption. I.e. if a single document has size larger than 2MiB (but less + // than `maxBsonObjectSize`) proceed with automatic encryption. + if self.encrypted && i != 0 { + let doc_entry_size = bson_util::array_entry_size_bytes(i, doc.as_bytes().len())?; + if (Checked::new(size) + doc_entry_size).get()? >= MAX_ENCRYPTED_WRITE_SIZE { + break; } - docs.push(doc); - size += doc_size; - } else { + } else if (Checked::new(size) + doc_size).get()? > max_doc_sequence_size.get()? { break; } - } - if docs.is_empty() { - return Err(ErrorKind::InvalidArgument { - message: "document exceeds maxBsonObjectSize".to_string(), - } - .into()); + self.inserted_ids.push(id); + docs.push(doc); + size += doc_size; } - let mut options = self.options.clone().unwrap_or_default(); - options.ordered = Some(self.is_ordered()); - remove_empty_write_concern!(Some(&mut options)); - let body = InsertCommand { - insert: self.ns.coll.clone(), - documents: docs, - options, + let mut body = rawdoc! { + Self::NAME: self.ns.coll.clone(), }; - Ok(Command::new("insert".to_string(), self.ns.db.clone(), body)) - } + let options_doc = bson::to_raw_document_buf(&self.options)?; + bson_util::extend_raw_document_buf(&mut body, options_doc)?; - fn serialize_command(&mut self, cmd: Command<Self::Command>) -> Result<Vec<u8>> { - let mut doc = bson::to_raw_document_buf(&cmd)?; - // need to append documents separately because #[serde(flatten)] breaks the custom - // serialization logic. See https://github.com/serde-rs/serde/issues/2106. - doc.append("documents", cmd.body.documents); - Ok(doc.into_bytes()) + if self.encrypted { + // Auto-encryption does not support document sequences + let mut raw_array = RawArrayBuf::new(); + for doc in docs { + raw_array.push(doc); + } + body.append("documents", raw_array); + Ok(Command::new(Self::NAME, &self.ns.db, body)) + } else { + let mut command = Command::new(Self::NAME, &self.ns.db, body); + command.add_document_sequence("documents", docs); + Ok(command) + } } fn handle_response( @@ -162,16 +158,12 @@ impl<'a, T: Serialize> OperationWithDefaults for Insert<'a, T> { _description: &StreamDescription, ) -> Result<Self::O> { let response: WriteResponseBody = raw_response.body_utf8_lossy()?; + let response_n = Checked::<usize>::try_from(response.n)?; let mut map = HashMap::new(); - if self.is_ordered() { + if self.options.ordered == Some(true) { // in ordered inserts, only the first n were attempted. - for (i, id) in self - .inserted_ids - .iter() - .enumerate() - .take(response.n as usize) - { + for (i, id) in self.inserted_ids.iter().enumerate().take(response_n.get()?) { map.insert(i, id.clone()); } } else { @@ -203,24 +195,10 @@ impl<'a, T: Serialize> OperationWithDefaults for Insert<'a, T> { } fn write_concern(&self) -> Option<&WriteConcern> { - self.options.as_ref().and_then(|o| o.write_concern.as_ref()) + self.options.write_concern.as_ref() } fn retryability(&self) -> Retryability { Retryability::Write } } - -#[derive(Serialize)] -pub(crate) struct InsertCommand { - insert: String, - - /// will be serialized in `serialize_command` - #[serde(skip)] - documents: RawArrayBuf, - - #[serde(flatten)] - options: InsertManyOptions, -} - -impl CommandBody for InsertCommand {}
src/operation/insert/test.rs+2 −213 modified@@ -1,17 +1,8 @@ -use bson::{ - oid::ObjectId, - spec::BinarySubtype, - Binary, - DateTime, - JavaScriptCodeWithScope, - Regex, - Timestamp, -}; use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; use crate::{ - bson::{doc, Bson, Document}, + bson::{doc, Document}, cmap::StreamDescription, concern::WriteConcern, error::{BulkWriteError, ErrorKind, WriteConcernError}, @@ -23,7 +14,6 @@ use crate::{ struct TestFixtures { op: Insert<'static, Document>, documents: Vec<Document>, - options: InsertManyOptions, } /// Get an Insert operation and the documents/options used to construct it. @@ -50,221 +40,20 @@ fn fixtures(opts: Option<InsertManyOptions>) -> TestFixtures { DOCUMENTS.iter().collect(), Some(options.clone()), false, + false, ); TestFixtures { op, documents: DOCUMENTS.clone(), - options, } } -#[test] -fn build() { - let mut fixtures = fixtures(None); - - let description = StreamDescription::new_testing(); - let cmd = fixtures.op.build(&description).unwrap(); - - assert_eq!(cmd.name.as_str(), "insert"); - assert_eq!(cmd.target_db.as_str(), "test_db"); - - assert_eq!(cmd.body.insert, "test_coll".to_string()); - - let mut cmd_docs: Vec<Document> = cmd - .body - .documents - .as_ref() - .into_iter() - .map(|b| Document::from_reader(b.unwrap().as_document().unwrap().as_bytes()).unwrap()) - .collect(); - assert_eq!(cmd_docs.len(), fixtures.documents.len()); - - for (original_doc, cmd_doc) in fixtures.documents.iter().zip(cmd_docs.iter_mut()) { - assert!(cmd_doc.get("_id").is_some()); - if original_doc.get("_id").is_none() { - cmd_doc.remove("_id"); - } - assert_eq!(original_doc, cmd_doc); - } - - let serialized = fixtures.op.serialize_command(cmd).unwrap(); - let cmd_doc = Document::from_reader(serialized.as_slice()).unwrap(); - - assert_eq!( - cmd_doc.get("ordered"), - fixtures.options.ordered.map(Bson::Boolean).as_ref() - ); - assert_eq!( - cmd_doc.get("bypassDocumentValidation"), - fixtures - .options - .bypass_document_validation - .map(Bson::Boolean) - .as_ref() - ); - assert_eq!( - cmd_doc.get("writeConcern"), - fixtures - .options - .write_concern - .as_ref() - .map(|wc| bson::to_bson(wc).unwrap()) - .as_ref() - ); -} - -#[test] -fn build_ordered() { - let docs = vec![Document::new()]; - let mut insert = Insert::new(Namespace::empty(), docs.iter().collect(), None, false); - let cmd = insert - .build(&StreamDescription::new_testing()) - .expect("should succeed"); - let serialized = insert.serialize_command(cmd).unwrap(); - let cmd_doc = Document::from_reader(serialized.as_slice()).unwrap(); - assert_eq!(cmd_doc.get("ordered"), Some(&Bson::Boolean(true))); - - let mut insert = Insert::new( - Namespace::empty(), - docs.iter().collect(), - Some(InsertManyOptions::builder().ordered(false).build()), - false, - ); - let cmd = insert - .build(&StreamDescription::new_testing()) - .expect("should succeed"); - let serialized = insert.serialize_command(cmd).unwrap(); - let cmd_doc = Document::from_reader(serialized.as_slice()).unwrap(); - assert_eq!(cmd_doc.get("ordered"), Some(&Bson::Boolean(false))); - - let mut insert = Insert::new( - Namespace::empty(), - docs.iter().collect(), - Some(InsertManyOptions::builder().ordered(true).build()), - false, - ); - let cmd = insert - .build(&StreamDescription::new_testing()) - .expect("should succeed"); - let serialized = insert.serialize_command(cmd).unwrap(); - let cmd_doc = Document::from_reader(serialized.as_slice()).unwrap(); - assert_eq!(cmd_doc.get("ordered"), Some(&Bson::Boolean(true))); - - let mut insert = Insert::new( - Namespace::empty(), - docs.iter().collect(), - Some(InsertManyOptions::builder().build()), - false, - ); - let cmd = insert - .build(&StreamDescription::new_testing()) - .expect("should succeed"); - let serialized = insert.serialize_command(cmd).unwrap(); - let cmd_doc = Document::from_reader(serialized.as_slice()).unwrap(); - assert_eq!(cmd_doc.get("ordered"), Some(&Bson::Boolean(true))); -} - #[derive(Debug, Serialize, Deserialize)] struct Documents<D> { documents: Vec<D>, } -#[test] -fn generate_ids() { - let docs = vec![doc! { "x": 1 }, doc! { "_id": 1_i32, "x": 2 }]; - - let mut insert = Insert::new(Namespace::empty(), docs.iter().collect(), None, false); - let cmd = insert.build(&StreamDescription::new_testing()).unwrap(); - let serialized = insert.serialize_command(cmd).unwrap(); - - #[derive(Debug, Serialize, Deserialize)] - struct D { - x: i32, - - #[serde(rename = "_id")] - id: Bson, - } - - let docs: Documents<D> = bson::from_slice(serialized.as_slice()).unwrap(); - - assert_eq!(docs.documents.len(), 2); - let docs = docs.documents; - - docs[0].id.as_object_id().unwrap(); - assert_eq!(docs[0].x, 1); - - assert_eq!(docs[1].id, Bson::Int32(1)); - assert_eq!(docs[1].x, 2); - - // ensure the _id was prepended to the document - let docs: Documents<Document> = bson::from_slice(serialized.as_slice()).unwrap(); - assert_eq!(docs.documents[0].iter().next().unwrap().0, "_id") -} - -#[test] -fn serialize_all_types() { - let binary = Binary { - bytes: vec![36, 36, 36], - subtype: BinarySubtype::Generic, - }; - let date = DateTime::now(); - let regex = Regex { - pattern: "hello".to_string(), - options: "x".to_string(), - }; - let timestamp = Timestamp { - time: 123, - increment: 456, - }; - let code = Bson::JavaScriptCode("console.log(1)".to_string()); - let code_w_scope = JavaScriptCodeWithScope { - code: "console.log(a)".to_string(), - scope: doc! { "a": 1 }, - }; - let oid = ObjectId::new(); - let subdoc = doc! { "k": true, "b": { "hello": "world" } }; - - let decimal = { - let bytes = hex::decode("18000000136400D0070000000000000000000000003A3000").unwrap(); - let d = Document::from_reader(bytes.as_slice()).unwrap(); - d.get("d").unwrap().clone() - }; - - let docs = vec![doc! { - "x": 1_i32, - "y": 2_i64, - "s": "oke", - "array": [ true, "oke", { "12": 24 } ], - "bson": 1234.5, - "oid": oid, - "null": Bson::Null, - "subdoc": subdoc, - "b": true, - "d": 12.5, - "binary": binary, - "date": date, - "regex": regex, - "ts": timestamp, - "i": { "a": 300, "b": 12345 }, - "undefined": Bson::Undefined, - "code": code, - "code_w_scope": code_w_scope, - "decimal": decimal, - "symbol": Bson::Symbol("ok".to_string()), - "min_key": Bson::MinKey, - "max_key": Bson::MaxKey, - "_id": ObjectId::new(), - }]; - - let mut insert = Insert::new(Namespace::empty(), docs.iter().collect(), None, false); - let cmd = insert.build(&StreamDescription::new_testing()).unwrap(); - let serialized = insert.serialize_command(cmd).unwrap(); - let cmd: Documents<Document> = bson::from_slice(serialized.as_slice()).unwrap(); - - assert_eq!(cmd.documents, docs); -} - #[test] fn handle_success() { let mut fixtures = fixtures(None);
src/operation/raw_output.rs+0 −4 modified@@ -19,10 +19,6 @@ impl<Op: Operation> Operation for RawOutput<Op> { self.0.build(description) } - fn serialize_command(&mut self, cmd: Command<Self::Command>) -> Result<Vec<u8>> { - self.0.serialize_command(cmd) - } - fn extract_at_cluster_time( &self, response: &bson::RawDocument,
src/operation.rs+5 −13 modified@@ -79,6 +79,11 @@ pub(crate) use update::{Update, UpdateOrReplace}; const SERVER_4_2_0_WIRE_VERSION: i32 = 8; const SERVER_4_4_0_WIRE_VERSION: i32 = 9; +// The maximum number of bytes that may be included in a write payload when auto-encryption is +// enabled. +const MAX_ENCRYPTED_WRITE_SIZE: usize = 2_097_152; +// The amount of overhead bytes to account for when building a document sequence. +const COMMAND_OVERHEAD_SIZE: usize = 16_000; /// A trait modeling the behavior of a server side operation. /// @@ -98,10 +103,6 @@ pub(crate) trait Operation { /// The operation may store some additional state that is required for handling the response. fn build(&mut self, description: &StreamDescription) -> Result<Command<Self::Command>>; - /// Perform custom serialization of the built command. - /// By default, this will just call through to the `Serialize` implementation of the command. - fn serialize_command(&mut self, cmd: Command<Self::Command>) -> Result<Vec<u8>>; - /// Parse the response for the atClusterTime field. /// Depending on the operation, this may be found in different locations. fn extract_at_cluster_time(&self, _response: &RawDocument) -> Result<Option<Timestamp>>; @@ -413,12 +414,6 @@ pub(crate) trait OperationWithDefaults { /// The operation may store some additional state that is required for handling the response. fn build(&mut self, description: &StreamDescription) -> Result<Command<Self::Command>>; - /// Perform custom serialization of the built command. - /// By default, this will just call through to the `Serialize` implementation of the command. - fn serialize_command(&mut self, cmd: Command<Self::Command>) -> Result<Vec<u8>> { - Ok(bson::to_vec(&cmd)?) - } - /// Parse the response for the atClusterTime field. /// Depending on the operation, this may be found in different locations. fn extract_at_cluster_time(&self, _response: &RawDocument) -> Result<Option<Timestamp>> { @@ -489,9 +484,6 @@ impl<T: OperationWithDefaults> Operation for T { fn build(&mut self, description: &StreamDescription) -> Result<Command<Self::Command>> { self.build(description) } - fn serialize_command(&mut self, cmd: Command<Self::Command>) -> Result<Vec<u8>> { - self.serialize_command(cmd) - } fn extract_at_cluster_time(&self, response: &RawDocument) -> Result<Option<Timestamp>> { self.extract_at_cluster_time(response) }
src/operation/run_cursor_command.rs+0 −4 modified@@ -38,10 +38,6 @@ impl<'conn> Operation for RunCursorCommand<'conn> { self.run_command.build(description) } - fn serialize_command(&mut self, cmd: Command<Self::Command>) -> Result<Vec<u8>> { - self.run_command.serialize_command(cmd) - } - fn extract_at_cluster_time( &self, response: &bson::RawDocument,
src/operation/update.rs+0 −4 modified@@ -182,10 +182,6 @@ impl<'a, T: Serialize> OperationWithDefaults for Update<'a, T> { )) } - fn serialize_command(&mut self, cmd: Command<Self::Command>) -> Result<Vec<u8>> { - cmd.into_bson_bytes() - } - fn handle_response( &self, raw_response: RawCommandResponse,
src/sdam/description/topology.rs+2 −2 modified@@ -204,7 +204,7 @@ impl TopologyDescription { self.servers.get(address) } - pub(crate) fn update_command_with_read_pref<T>( + pub(crate) fn update_command_with_read_pref<T: Serialize>( &self, address: &ServerAddress, command: &mut Command<T>, @@ -252,7 +252,7 @@ impl TopologyDescription { } } - fn update_command_read_pref_for_mongos<T>( + fn update_command_read_pref_for_mongos<T: Serialize>( &self, command: &mut Command<T>, criteria: Option<&SelectionCriteria>,
src/sdam/description/topology/server_selection.rs+12 −4 modified@@ -291,7 +291,7 @@ impl TopologyDescription { primary: &ServerDescription, max_staleness: Duration, ) { - let max_staleness_ms = max_staleness.as_millis() as i64; + let max_staleness_ms = max_staleness.as_millis().try_into().unwrap_or(i64::MAX); servers.retain(|server| { let server_staleness = self.calculate_secondary_staleness_with_primary(server, primary); @@ -307,7 +307,7 @@ impl TopologyDescription { servers: &mut Vec<&ServerDescription>, max_staleness: Duration, ) { - let max_staleness = max_staleness.as_millis() as i64; + let max_staleness = max_staleness.as_millis().try_into().unwrap_or(i64::MAX); let max_write_date = self .servers .values() @@ -347,7 +347,11 @@ impl TopologyDescription { let secondary_last_update = secondary.last_update_time?.timestamp_millis(); let secondary_last_write = secondary.last_write_date().ok()??.timestamp_millis(); - let heartbeat_frequency = self.heartbeat_frequency().as_millis() as i64; + let heartbeat_frequency = self + .heartbeat_frequency() + .as_millis() + .try_into() + .unwrap_or(i64::MAX); let staleness = (secondary_last_update - secondary_last_write) - (primary_last_update - primary_last_write) @@ -362,7 +366,11 @@ impl TopologyDescription { max_last_write_date: i64, ) -> Option<i64> { let secondary_last_write = secondary.last_write_date().ok()??.timestamp_millis(); - let heartbeat_frequency = self.heartbeat_frequency().as_millis() as i64; + let heartbeat_frequency = self + .heartbeat_frequency() + .as_millis() + .try_into() + .unwrap_or(i64::MAX); let staleness = max_last_write_date - secondary_last_write + heartbeat_frequency; Some(staleness)
src/sdam/description/topology/server_selection/test/in_window.rs+15 −12 modified@@ -200,18 +200,21 @@ async fn load_balancing_test() { counts.sort(); let share_of_selections = (*counts[0] as f64) / ((*counts[0] + *counts[1]) as f64); - assert!( - share_of_selections <= max_share, - "expected no more than {}% of selections, instead got {}%", - (max_share * 100.0) as u32, - (share_of_selections * 100.0) as u32 - ); - assert!( - share_of_selections >= min_share, - "expected at least {}% of selections, instead got {}%", - (min_share * 100.0) as u32, - (share_of_selections * 100.0) as u32 - ); + #[allow(clippy::cast_possible_truncation)] + { + assert!( + share_of_selections <= max_share, + "expected no more than {}% of selections, instead got {}%", + (max_share * 100.0) as u32, + (share_of_selections * 100.0) as u32 + ); + assert!( + share_of_selections >= min_share, + "expected at least {}% of selections, instead got {}%", + (min_share * 100.0) as u32, + (share_of_selections * 100.0) as u32 + ); + } } let mut handler = EventHandler::new();
src/sdam/description/topology/test.rs+1 −0 modified@@ -6,6 +6,7 @@ use std::time::Duration; pub use event::TestSdamEvent; +#[allow(clippy::cast_possible_truncation)] pub(crate) fn f64_ms_as_duration(f: f64) -> Duration { Duration::from_micros((f * 1000.0) as u64) }
src/sdam/topology.rs+2 −1 modified@@ -10,6 +10,7 @@ use futures_util::{ stream::{FuturesUnordered, StreamExt}, FutureExt, }; +use serde::Serialize; use tokio::sync::{ mpsc::{self, UnboundedReceiver, UnboundedSender}, watch::{self, Ref}, @@ -198,7 +199,7 @@ impl Topology { } /// Updates the given `command` as needed based on the `criteria`. - pub(crate) fn update_command_with_read_pref<T>( + pub(crate) fn update_command_with_read_pref<T: Serialize>( &self, server_address: &ServerAddress, command: &mut Command<T>,
src/serde_util.rs+23 −6 modified@@ -7,6 +7,7 @@ use crate::{ bson::{doc, Bson, Document, RawDocumentBuf}, bson_util::get_u64, error::{Error, Result}, + options::WriteConcern, }; pub(crate) mod duration_option_as_int_seconds { @@ -17,9 +18,13 @@ pub(crate) mod duration_option_as_int_seconds { serializer: S, ) -> std::result::Result<S::Ok, S::Error> { match val { - Some(duration) if duration.as_secs() > i32::MAX as u64 => { - serializer.serialize_i64(duration.as_secs() as i64) - } + Some(duration) if duration.as_secs() > i32::MAX as u64 => serializer.serialize_i64( + duration + .as_secs() + .try_into() + .map_err(serde::ser::Error::custom)?, + ), + #[allow(clippy::cast_possible_truncation)] Some(duration) => serializer.serialize_i32(duration.as_secs() as i32), None => serializer.serialize_none(), } @@ -41,9 +46,13 @@ pub(crate) fn serialize_duration_option_as_int_millis<S: Serializer>( serializer: S, ) -> std::result::Result<S::Ok, S::Error> { match val { - Some(duration) if duration.as_millis() > i32::MAX as u128 => { - serializer.serialize_i64(duration.as_millis() as i64) - } + Some(duration) if duration.as_millis() > i32::MAX as u128 => serializer.serialize_i64( + duration + .as_millis() + .try_into() + .map_err(serde::ser::Error::custom)?, + ), + #[allow(clippy::cast_possible_truncation)] Some(duration) => serializer.serialize_i32(duration.as_millis() as i32), None => serializer.serialize_none(), } @@ -76,6 +85,7 @@ pub(crate) fn serialize_u32_option_as_batch_size<S: Serializer>( serializer: S, ) -> std::result::Result<S::Ok, S::Error> { match val { + #[allow(clippy::cast_possible_wrap)] Some(val) if *val <= std::i32::MAX as u32 => (doc! { "batchSize": (*val as i32) }) @@ -140,6 +150,7 @@ where } let date_time = match AwsDateTime::deserialize(deserializer)? { + #[allow(clippy::cast_possible_truncation)] AwsDateTime::Double(seconds) => { let millis = seconds * 1000.0; bson::DateTime::from_millis(millis as i64) @@ -167,6 +178,12 @@ pub(crate) fn to_raw_document_buf_with_options<T: Serialize>( Ok(raw_doc) } +pub(crate) fn write_concern_is_empty(write_concern: &Option<WriteConcern>) -> bool { + write_concern + .as_ref() + .map_or(true, |write_concern| write_concern.is_empty()) +} + #[cfg(test)] pub(crate) fn deserialize_nonempty_vec<'de, D, T>( deserializer: D,
src/test/coll.rs+101 −2 modified@@ -1,6 +1,12 @@ -use std::{fmt::Debug, time::Duration}; +use std::{fmt::Debug, sync::Arc, time::Duration}; -use crate::Namespace; +use crate::{ + event::command::CommandEvent, + test::{Event, EventHandler}, + Client, + Namespace, +}; +use bson::{rawdoc, RawDocumentBuf}; use futures::stream::{StreamExt, TryStreamExt}; use lazy_static::lazy_static; use semver::VersionReq; @@ -1280,3 +1286,96 @@ async fn configure_human_readable_serialization() { .await .unwrap(); } + +#[cfg_attr(feature = "tokio-runtime", tokio::test)] +#[cfg_attr(feature = "async-std-runtime", async_std::test)] +async fn insert_many_document_sequences() { + if cfg!(feature = "in-use-encryption-unstable") { + log_uncaptured( + "skipping insert_many_document_sequences: auto-encryption does not support document \ + sequences", + ); + return; + } + + let handler = Arc::new(EventHandler::new()); + let client = Client::test_builder() + .event_handler(handler.clone()) + .build() + .await; + let mut subscriber = handler.subscribe(); + + let max_object_size = client.server_info.max_bson_object_size; + let max_message_size = client.server_info.max_message_size_bytes; + + let collection = client + .database("insert_many_document_sequences") + .collection::<RawDocumentBuf>("insert_many_document_sequences"); + collection.drop(None).await.unwrap(); + + // A payload with > max_bson_object_size bytes but < max_message_size bytes should require only + // one round trip + let docs = vec![ + rawdoc! { "s": "a".repeat((max_object_size / 2) as usize) }, + rawdoc! { "s": "b".repeat((max_object_size / 2) as usize) }, + ]; + collection.insert_many(docs, None).await.unwrap(); + + let event = subscriber + .filter_map_event(Duration::from_millis(500), |e| match e { + Event::Command(command_event) => match command_event { + CommandEvent::Started(started) if started.command_name.as_str() == "insert" => { + Some(started) + } + _ => None, + }, + _ => None, + }) + .await + .expect("did not observe command started event for insert"); + let insert_documents = event.command.get_array("documents").unwrap(); + assert_eq!(insert_documents.len(), 2); + + // Build up a list of documents that exceeds max_message_size + let mut docs = Vec::new(); + let mut size = 0; + while size <= max_message_size { + // Leave some room for key/metadata bytes in document + let string_length = max_object_size - 500; + let doc = rawdoc! { "s": "a".repeat(string_length as usize) }; + size += doc.as_bytes().len() as i32; + docs.push(doc); + } + let total_docs = docs.len(); + collection.insert_many(docs, None).await.unwrap(); + + let first_event = subscriber + .filter_map_event(Duration::from_millis(500), |e| match e { + Event::Command(command_event) => match command_event { + CommandEvent::Started(started) if started.command_name.as_str() == "insert" => { + Some(started) + } + _ => None, + }, + _ => None, + }) + .await + .expect("did not observe command started event for insert"); + let first_batch_len = first_event.command.get_array("documents").unwrap().len(); + assert!(first_batch_len < total_docs); + + let second_event = subscriber + .filter_map_event(Duration::from_millis(500), |e| match e { + Event::Command(command_event) => match command_event { + CommandEvent::Started(started) if started.command_name.as_str() == "insert" => { + Some(started) + } + _ => None, + }, + _ => None, + }) + .await + .expect("did not observe command started event for insert"); + let second_batch_len = second_event.command.get_array("documents").unwrap().len(); + assert_eq!(first_batch_len + second_batch_len, total_docs); +}
src/test.rs+3 −0 modified@@ -1,3 +1,6 @@ +#![allow(clippy::cast_possible_truncation)] +#![allow(clippy::cast_possible_wrap)] + #[cfg(all(not(feature = "sync"), not(feature = "tokio-sync")))] mod atlas_connectivity; mod atlas_planned_maintenance_testing;
a3fe6c84ce62RUST-1881 Check integer conversions (#1045)
18 files changed · +650 −101
src/bson_util.rs+6 −3 modified@@ -5,12 +5,14 @@ use std::{ use crate::{ bson::{Bson, Document, RawArrayBuf, RawBson, RawBsonRef, RawDocumentBuf}, + checked::Checked, error::{ErrorKind, Result}, runtime::SyncLittleEndianRead, }; /// Coerce numeric types into an `i64` if it would be lossless to do so. If this Bson is not numeric /// or the conversion would be lossy (e.g. 1.5 -> 1), this returns `None`. +#[allow(clippy::cast_possible_truncation)] pub(crate) fn get_int(val: &Bson) -> Option<i64> { match *val { Bson::Int32(i) => Some(i64::from(i)), @@ -33,6 +35,7 @@ pub(crate) fn get_int_raw(val: RawBsonRef<'_>) -> Option<i64> { /// Coerce numeric types into an `u64` if it would be lossless to do so. If this Bson is not numeric /// or the conversion would be lossy (e.g. 1.5 -> 1), this returns `None`. +#[allow(clippy::cast_possible_truncation)] pub(crate) fn get_u64(val: &Bson) -> Option<u64> { match *val { Bson::Int32(i) => u64::try_from(i).ok(), @@ -89,18 +92,18 @@ pub(crate) fn update_document_check(update: &Document) -> Result<()> { } /// The size in bytes of the provided document's entry in a BSON array at the given index. -pub(crate) fn array_entry_size_bytes(index: usize, doc_len: usize) -> u64 { +pub(crate) fn array_entry_size_bytes(index: usize, doc_len: usize) -> Result<usize> { // * type (1 byte) // * number of decimal digits in key // * null terminator for the key (1 byte) // * size of value - 1 + num_decimal_digits(index) + 1 + doc_len as u64 + (Checked::new(1) + num_decimal_digits(index) + 1 + doc_len).get() } /// The number of digits in `n` in base 10. /// Useful for calculating the size of an array entry in BSON. -fn num_decimal_digits(mut n: usize) -> u64 { +fn num_decimal_digits(mut n: usize) -> usize { let mut digits = 0; loop {
src/checked.rs+502 −0 added@@ -0,0 +1,502 @@ +// Modified from https://github.com/zeta12ti/Checked/blob/master/src/num.rs +// Original license: +// MIT License +// +// Copyright (c) 2017 zeta12ti +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +use std::{cmp::Ordering, convert::TryFrom, fmt, ops::*}; + +/// The Checked type. See the [module level documentation for more.](index.html) +#[derive(PartialEq, Eq, Clone, Copy, Hash)] +pub struct Checked<T>(pub Option<T>); + +impl<T> Checked<T> { + /// Creates a new Checked instance from some sort of integer. + #[inline] + pub fn new(x: T) -> Checked<T> { + Checked(Some(x)) + } + + pub fn try_from<F>(value: F) -> crate::error::Result<Self> + where + T: TryFrom<F>, + T::Error: std::fmt::Display, + { + value + .try_into() + .map(|v| Self(Some(v))) + .map_err(|e| crate::error::Error::invalid_argument(format! {"{}", e})) + } + + pub fn get(self) -> crate::error::Result<T> { + self.0 + .ok_or_else(|| crate::error::Error::invalid_argument("checked arithmetic failure")) + } + + pub fn try_into<F>(self) -> crate::error::Result<F> + where + T: TryInto<F>, + T::Error: std::fmt::Display, + { + self.get().and_then(|v| { + v.try_into() + .map_err(|e| crate::error::Error::invalid_argument(format!("{}", e))) + }) + } +} + +// The derived Default only works if T has Default +// Even though this is what it would be anyway +// May change this to T's default (if it has one) +impl<T> Default for Checked<T> { + #[inline] + fn default() -> Checked<T> { + Checked(None) + } +} + +impl<T: fmt::Debug> fmt::Debug for Checked<T> { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match **self { + Some(ref x) => x.fmt(f), + None => "overflow".fmt(f), + } + } +} + +impl<T: fmt::Display> fmt::Display for Checked<T> { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match **self { + Some(ref x) => x.fmt(f), + None => "overflow".fmt(f), + } + } +} + +// I'd like to do +// `impl<T, U> From<U> where T: From<U> for Checked<T>`` +// in the obvious way, but that "conflicts" with the default `impl From<T> for T`. +// This would subsume both the below Froms since Option has the right From impl. +impl<T> From<T> for Checked<T> { + #[inline] + fn from(x: T) -> Checked<T> { + Checked(Some(x)) + } +} + +impl<T> From<Option<T>> for Checked<T> { + #[inline] + fn from(x: Option<T>) -> Checked<T> { + Checked(x) + } +} + +impl<T> Deref for Checked<T> { + type Target = Option<T>; + + #[inline] + fn deref(&self) -> &Option<T> { + &self.0 + } +} + +impl<T> DerefMut for Checked<T> { + #[inline] + fn deref_mut(&mut self) -> &mut Option<T> { + &mut self.0 + } +} + +impl<T: PartialOrd> PartialOrd for Checked<T> { + fn partial_cmp(&self, other: &Checked<T>) -> Option<Ordering> { + // I'm not really sure why we can't match **self etc. here. + // Even with refs everywhere it complains + // Note what happens in this implementation: + // we take the reference self, and call deref (the method) on it + // By Deref coercion, self gets derefed to a Checked<T> + // Now Checked<T>'s deref gets called, returning a &Option<T> + // That's what gets matched + match (self.deref(), other.deref()) { + (Some(x), Some(y)) => PartialOrd::partial_cmp(x, y), + _ => None, + } + } +} + +// implements the unary operator `op &T` +// based on `op T` where `T` is expected to be `Copy`able +macro_rules! forward_ref_unop { + (impl $imp:ident, $method:ident for $t:ty {}) => { + impl<'a> $imp for &'a $t { + type Output = <$t as $imp>::Output; + + #[inline] + fn $method(self) -> <$t as $imp>::Output { + $imp::$method(*self) + } + } + }; +} + +// implements binary operators "&T op U", "T op &U", "&T op &U" +// based on "T op U" where T and U are expected to be `Copy`able +macro_rules! forward_ref_binop { + (impl $imp:ident, $method:ident for $t:ty, $u:ty {}) => { + impl<'a> $imp<$u> for &'a $t { + type Output = <$t as $imp<$u>>::Output; + + #[inline] + fn $method(self, other: $u) -> <$t as $imp<$u>>::Output { + $imp::$method(*self, other) + } + } + + impl<'a> $imp<&'a $u> for $t { + type Output = <$t as $imp<$u>>::Output; + + #[inline] + fn $method(self, other: &'a $u) -> <$t as $imp<$u>>::Output { + $imp::$method(self, *other) + } + } + + impl<'a, 'b> $imp<&'a $u> for &'b $t { + type Output = <$t as $imp<$u>>::Output; + + #[inline] + fn $method(self, other: &'a $u) -> <$t as $imp<$u>>::Output { + $imp::$method(*self, *other) + } + } + }; +} + +macro_rules! impl_sh { + ($t:ident, $f:ident) => { + impl Shl<Checked<$f>> for Checked<$t> { + type Output = Checked<$t>; + + fn shl(self, other: Checked<$f>) -> Checked<$t> { + match (*self, *other) { + (Some(x), Some(y)) => Checked(x.checked_shl(y)), + _ => Checked(None), + } + } + } + + impl Shl<$f> for Checked<$t> { + type Output = Checked<$t>; + + fn shl(self, other: $f) -> Checked<$t> { + match *self { + Some(x) => Checked(x.checked_shl(other)), + None => Checked(None), + } + } + } + + forward_ref_binop! { impl Shl, shl for Checked<$t>, Checked<$f> {} } + forward_ref_binop! { impl Shl, shl for Checked<$t>, $f {} } + + impl ShlAssign<$f> for Checked<$t> { + #[inline] + fn shl_assign(&mut self, other: $f) { + *self = *self << other; + } + } + + impl ShlAssign<Checked<$f>> for Checked<$t> { + #[inline] + fn shl_assign(&mut self, other: Checked<$f>) { + *self = *self << other; + } + } + + impl Shr<Checked<$f>> for Checked<$t> { + type Output = Checked<$t>; + + fn shr(self, other: Checked<$f>) -> Checked<$t> { + match (*self, *other) { + (Some(x), Some(y)) => Checked(x.checked_shr(y)), + _ => Checked(None), + } + } + } + + impl Shr<$f> for Checked<$t> { + type Output = Checked<$t>; + + fn shr(self, other: $f) -> Checked<$t> { + match *self { + Some(x) => Checked(x.checked_shr(other)), + None => Checked(None), + } + } + } + + forward_ref_binop! { impl Shr, shr for Checked<$t>, Checked<$f> {} } + forward_ref_binop! { impl Shr, shr for Checked<$t>, $f {} } + + impl ShrAssign<$f> for Checked<$t> { + #[inline] + fn shr_assign(&mut self, other: $f) { + *self = *self >> other; + } + } + + impl ShrAssign<Checked<$f>> for Checked<$t> { + #[inline] + fn shr_assign(&mut self, other: Checked<$f>) { + *self = *self >> other; + } + } + }; +} + +macro_rules! impl_sh_reverse { + ($t:ident, $f:ident) => { + impl Shl<Checked<$t>> for $f { + type Output = Checked<$f>; + + fn shl(self, other: Checked<$t>) -> Checked<$f> { + match *other { + Some(x) => Checked(self.checked_shl(x)), + None => Checked(None), + } + } + } + + forward_ref_binop! { impl Shl, shl for $f, Checked<$t> {} } + + impl Shr<Checked<$t>> for $f { + type Output = Checked<$f>; + + fn shr(self, other: Checked<$t>) -> Checked<$f> { + match *other { + Some(x) => Checked(self.checked_shr(x)), + None => Checked(None), + } + } + } + + forward_ref_binop! { impl Shr, shr for $f, Checked<$t> {} } + }; +} + +macro_rules! impl_sh_all { + ($($t:ident)*) => ($( + // When checked_shX is added for other shift sizes, uncomment some of these. + // impl_sh! { $t, u8 } + // impl_sh! { $t, u16 } + impl_sh! { $t, u32 } + //impl_sh! { $t, u64 } + //impl_sh! { $t, usize } + + //impl_sh! { $t, i8 } + //impl_sh! { $t, i16 } + //impl_sh! { $t, i32 } + //impl_sh! { $t, i64 } + //impl_sh! { $t, isize } + + // impl_sh_reverse! { u8, $t } + // impl_sh_reverse! { u16, $t } + impl_sh_reverse! { u32, $t } + //impl_sh_reverse! { u64, $t } + //impl_sh_reverse! { usize, $t } + + //impl_sh_reverse! { i8, $t } + //impl_sh_reverse! { i16, $t } + //impl_sh_reverse! { i32, $t } + //impl_sh_reverse! { i64, $t } + //impl_sh_reverse! { isize, $t } + )*) +} + +impl_sh_all! { u8 u16 u32 u64 usize i8 i16 i32 i64 isize } + +// implements unary operators for checked types +macro_rules! impl_unop { + (impl $imp:ident, $method:ident, $checked_method:ident for $t:ty {}) => { + impl $imp for Checked<$t> { + type Output = Checked<$t>; + + fn $method(self) -> Checked<$t> { + match *self { + Some(x) => Checked(x.$checked_method()), + None => Checked(None), + } + } + } + + forward_ref_unop! { impl $imp, $method for Checked<$t> {} } + }; +} + +// implements unary operators for checked types (with no checked method) +macro_rules! impl_unop_unchecked { + (impl $imp:ident, $method:ident for $t:ty {$op:tt}) => { + impl $imp for Checked<$t> { + type Output = Checked<$t>; + + fn $method(self) -> Checked<$t> { + match *self { + Some(x) => Checked(Some($op x)), + None => Checked(None) + } + } + } + + forward_ref_unop! { impl $imp, $method for Checked<$t> {} } + } +} + +// implements binary operators for checked types +macro_rules! impl_binop { + (impl $imp:ident, $method:ident, $checked_method:ident for $t:ty {}) => { + impl $imp for Checked<$t> { + type Output = Checked<$t>; + + fn $method(self, other: Checked<$t>) -> Checked<$t> { + match (*self, *other) { + (Some(x), Some(y)) => Checked(x.$checked_method(y)), + _ => Checked(None), + } + } + } + + impl $imp<$t> for Checked<$t> { + type Output = Checked<$t>; + + fn $method(self, other: $t) -> Checked<$t> { + match *self { + Some(x) => Checked(x.$checked_method(other)), + _ => Checked(None), + } + } + } + + impl $imp<Checked<$t>> for $t { + type Output = Checked<$t>; + + fn $method(self, other: Checked<$t>) -> Checked<$t> { + match *other { + Some(x) => Checked(self.$checked_method(x)), + None => Checked(None), + } + } + } + + forward_ref_binop! { impl $imp, $method for Checked<$t>, Checked<$t> {} } + forward_ref_binop! { impl $imp, $method for Checked<$t>, $t {} } + forward_ref_binop! { impl $imp, $method for $t, Checked<$t> {} } + }; +} + +// implements binary operators for checked types (no checked method) +macro_rules! impl_binop_unchecked { + (impl $imp:ident, $method:ident for $t:ty {$op:tt}) => { + impl $imp for Checked<$t> { + type Output = Checked<$t>; + + fn $method(self, other: Checked<$t>) -> Checked<$t> { + match (*self, *other) { + (Some(x), Some(y)) => Checked(Some(x $op y)), + _ => Checked(None), + } + } + } + + impl $imp<$t> for Checked<$t> { + type Output = Checked<$t>; + + fn $method(self, other: $t) -> Checked<$t> { + match *self { + Some(x) => Checked(Some(x $op other)), + _ => Checked(None), + } + } + } + + impl $imp<Checked<$t>> for $t { + type Output = Checked<$t>; + + fn $method(self, other: Checked<$t>) -> Checked<$t> { + match *other { + Some(x) => Checked(Some(self $op x)), + None => Checked(None), + } + } + } + + forward_ref_binop! { impl $imp, $method for Checked<$t>, Checked<$t> {} } + forward_ref_binop! { impl $imp, $method for Checked<$t>, $t {} } + forward_ref_binop! { impl $imp, $method for $t, Checked<$t> {} } + } +} + +// implements assignment operators for checked types +macro_rules! impl_binop_assign { + (impl $imp:ident, $method:ident for $t:ty {$op:tt}) => { + impl $imp for Checked<$t> { + #[inline] + fn $method(&mut self, other: Checked<$t>) { + *self = *self $op other; + } + } + + impl $imp<$t> for Checked<$t> { + #[inline] + fn $method(&mut self, other: $t) { + *self = *self $op other; + } + } + }; +} + +macro_rules! checked_impl { + ($($t:ty)*) => { + $( + impl_binop! { impl Add, add, checked_add for $t {} } + impl_binop_assign! { impl AddAssign, add_assign for $t {+} } + impl_binop! { impl Sub, sub, checked_sub for $t {} } + impl_binop_assign! { impl SubAssign, sub_assign for $t {-} } + impl_binop! { impl Mul, mul, checked_mul for $t {} } + impl_binop_assign! { impl MulAssign, mul_assign for $t {*} } + impl_binop! { impl Div, div, checked_div for $t {} } + impl_binop_assign! { impl DivAssign, div_assign for $t {/} } + impl_binop! { impl Rem, rem, checked_rem for $t {} } + impl_binop_assign! { impl RemAssign, rem_assign for $t {%} } + impl_unop_unchecked! { impl Not, not for $t {!} } + impl_binop_unchecked! { impl BitXor, bitxor for $t {^} } + impl_binop_assign! { impl BitXorAssign, bitxor_assign for $t {^} } + impl_binop_unchecked! { impl BitOr, bitor for $t {|} } + impl_binop_assign! { impl BitOrAssign, bitor_assign for $t {|} } + impl_binop_unchecked! { impl BitAnd, bitand for $t {&} } + impl_binop_assign! { impl BitAndAssign, bitand_assign for $t {&} } + impl_unop! { impl Neg, neg, checked_neg for $t {} } + + )* + }; +} + +checked_impl! { u8 u16 u32 u64 usize i8 i16 i32 i64 isize }
src/client/options.rs+5 −1 modified@@ -740,7 +740,11 @@ impl Serialize for ClientOptions { writeconcern: &self.write_concern, loadbalanced: &self.load_balanced, zlibcompressionlevel: &None, - srvmaxhosts: self.srv_max_hosts.map(|v| v as i32), + srvmaxhosts: self + .srv_max_hosts + .map(|v| v.try_into()) + .transpose() + .map_err(serde::ser::Error::custom)?, }; client_options.serialize(serializer)
src/cmap/conn/wire/message.rs+42 −46 modified@@ -12,6 +12,7 @@ use super::{ use crate::{ bson::RawDocumentBuf, bson_util, + checked::Checked, cmap::{conn::wire::util::SyncCountReader, Command}, compression::{Compressor, Decoder}, error::{Error, ErrorKind, Result}, @@ -121,21 +122,22 @@ impl Message { mut reader: T, header: &Header, ) -> Result<Self> { - // TODO: RUST-616 ensure length is < maxMessageSizeBytes - let length_remaining = header.length - Header::LENGTH as i32; - let mut buf = vec![0u8; length_remaining as usize]; + let length = Checked::<usize>::try_from(header.length)?; + let length_remaining = length - Header::LENGTH; + let mut buf = vec![0u8; length_remaining.get()?]; reader.read_exact(&mut buf).await?; let reader = buf.as_slice(); - Self::read_op_common(reader, length_remaining, header) + Self::read_op_common(reader, length_remaining.get()?, header) } async fn read_from_op_compressed<T: AsyncRead + Unpin + Send>( mut reader: T, header: &Header, ) -> Result<Self> { - let length_remaining = header.length - Header::LENGTH as i32; - let mut buf = vec![0u8; length_remaining as usize]; + let length = Checked::<usize>::try_from(header.length)?; + let length_remaining = length - Header::LENGTH; + let mut buf = vec![0u8; length_remaining.get()?]; reader.read_exact(&mut buf).await?; let mut reader = buf.as_slice(); @@ -153,7 +155,7 @@ impl Message { } // Read uncompressed size - let uncompressed_size = reader.read_i32_sync()?; + let uncompressed_size = Checked::<usize>::try_from(reader.read_i32_sync()?)?; // Read compressor id let compressor_id: u8 = reader.read_u8_sync()?; @@ -165,7 +167,7 @@ impl Message { let decoded_message = decoder.decode(reader)?; // Check that claimed length matches original length - if decoded_message.len() as i32 != uncompressed_size { + if decoded_message.len() != uncompressed_size.get()? { return Err(ErrorKind::InvalidResponse { message: format!( "The server's message claims that the uncompressed length is {}, but was \ @@ -181,21 +183,18 @@ impl Message { let reader = decoded_message.as_slice(); let length_remaining = decoded_message.len(); - Self::read_op_common(reader, length_remaining as i32, header) + Self::read_op_common(reader, length_remaining, header) } - fn read_op_common( - mut reader: &[u8], - mut length_remaining: i32, - header: &Header, - ) -> Result<Self> { + fn read_op_common(mut reader: &[u8], length_remaining: usize, header: &Header) -> Result<Self> { + let mut length_remaining = Checked::new(length_remaining); let flags = MessageFlags::from_bits_truncate(reader.read_u32_sync()?); - length_remaining -= std::mem::size_of::<u32>() as i32; + length_remaining -= std::mem::size_of::<u32>(); let mut count_reader = SyncCountReader::new(&mut reader); let mut document_payload = None; let mut document_sequences = Vec::new(); - while length_remaining - count_reader.bytes_read() as i32 > 4 { + while (length_remaining - count_reader.bytes_read()).get()? > 4 { let next_section = MessageSection::read(&mut count_reader)?; match next_section { MessageSection::Document(document) => { @@ -216,22 +215,19 @@ impl Message { } } - length_remaining -= count_reader.bytes_read() as i32; + length_remaining -= count_reader.bytes_read(); let mut checksum = None; - if length_remaining == 4 && flags.contains(MessageFlags::CHECKSUM_PRESENT) { + if length_remaining.get()? == 4 && flags.contains(MessageFlags::CHECKSUM_PRESENT) { checksum = Some(reader.read_u32_sync()?); - } else if length_remaining != 0 { - return Err(ErrorKind::InvalidResponse { - message: format!( - "The server indicated that the reply would be {} bytes long, but it instead \ - was {}", - header.length, - header.length - length_remaining + count_reader.bytes_read() as i32, - ), - } - .into()); + } else if length_remaining.get()? != 0 { + let header_len = Checked::<usize>::try_from(header.length)?; + return Err(Error::invalid_response(format!( + "The server indicated that the reply would be {} bytes long, but it instead was {}", + header.length, + header_len - length_remaining + count_reader.bytes_read(), + ))); } Ok(Self { @@ -249,9 +245,9 @@ impl Message { /// Serializes the Message to bytes and writes them to `writer`. pub(crate) async fn write_to<T: AsyncWrite + Send + Unpin>(&self, mut writer: T) -> Result<()> { - let sections = self.get_sections_bytes(); + let sections = self.get_sections_bytes()?; - let total_length = Header::LENGTH + let total_length = Checked::new(Header::LENGTH) + std::mem::size_of::<u32>() + sections.len() + self @@ -261,7 +257,7 @@ impl Message { .unwrap_or(0); let header = Header { - length: total_length as i32, + length: total_length.try_into()?, request_id: self.request_id.unwrap_or_else(next_request_id), response_to: self.response_to, op_code: OpCode::Message, @@ -289,24 +285,24 @@ impl Message { let mut encoder = compressor.to_encoder()?; let compressor_id = compressor.id() as u8; - let sections = self.get_sections_bytes(); + let sections = self.get_sections_bytes()?; let flag_bytes = &self.flags.bits().to_le_bytes(); - let uncompressed_len = sections.len() + flag_bytes.len(); + let uncompressed_len = Checked::new(sections.len()) + flag_bytes.len(); // Compress the flags and sections. Depending on the handshake // this could use zlib, zstd or snappy encoder.write_all(flag_bytes)?; encoder.write_all(sections.as_slice())?; let compressed_bytes = encoder.finish()?; - let total_length = Header::LENGTH + let total_length = Checked::new(Header::LENGTH) + std::mem::size_of::<i32>() + std::mem::size_of::<i32>() + std::mem::size_of::<u8>() + compressed_bytes.len(); let header = Header { - length: total_length as i32, + length: total_length.try_into()?, request_id: self.request_id.unwrap_or_else(next_request_id), response_to: self.response_to, op_code: OpCode::Compressed, @@ -317,7 +313,7 @@ impl Message { // Write original (pre-compressed) opcode (always OP_MSG) writer.write_i32_le(OpCode::Message as i32).await?; // Write uncompressed size - writer.write_i32_le(uncompressed_len as i32).await?; + writer.write_i32_le(uncompressed_len.try_into()?).await?; // Write compressor id writer.write_u8(compressor_id).await?; // Write compressed message @@ -328,7 +324,7 @@ impl Message { Ok(()) } - fn get_sections_bytes(&self) -> Vec<u8> { + fn get_sections_bytes(&self) -> Result<Vec<u8>> { let mut sections = Vec::new(); // Payload type 0 @@ -349,8 +345,8 @@ impl Message { }); // Size bytes + identifier bytes + null-terminator byte + document bytes - let size = 4 + identifier_bytes.len() + 1 + documents_size; - sections.extend((size as i32).to_le_bytes()); + let size = Checked::new(4) + identifier_bytes.len() + 1 + documents_size; + sections.extend(size.try_into::<i32>()?.to_le_bytes()); sections.extend(identifier_bytes); sections.push(0); @@ -360,7 +356,7 @@ impl Message { } } - sections + Ok(sections) } } @@ -393,28 +389,28 @@ impl MessageSection { return Ok(MessageSection::Document(document)); } - let size = reader.read_i32_sync()?; - let mut length_remaining = size - std::mem::size_of::<i32>() as i32; + let size = Checked::<usize>::try_from(reader.read_i32_sync()?)?; + let mut length_remaining = size - std::mem::size_of::<i32>(); let mut identifier = String::new(); - length_remaining -= reader.read_to_string(&mut identifier)? as i32; + length_remaining -= reader.read_to_string(&mut identifier)?; let mut documents = Vec::new(); let mut count_reader = SyncCountReader::new(reader); - while length_remaining > count_reader.bytes_read() as i32 { + while length_remaining.get()? > count_reader.bytes_read() { let bytes = bson_util::read_document_bytes(&mut count_reader)?; let document = RawDocumentBuf::from_bytes(bytes)?; documents.push(document); } - if length_remaining != count_reader.bytes_read() as i32 { + if length_remaining.get()? != count_reader.bytes_read() { return Err(ErrorKind::InvalidResponse { message: format!( "The server indicated that the reply would be {} bytes long, but it instead \ was {}", size, - length_remaining + count_reader.bytes_read() as i32, + length_remaining + count_reader.bytes_read(), ), } .into());
src/collation.rs+1 −1 modified@@ -128,7 +128,7 @@ impl Serialize for CollationStrength { S: serde::Serializer, { let level = u32::from(*self); - serializer.serialize_i32(level as i32) + serializer.serialize_i32(level.try_into().map_err(serde::ser::Error::custom)?) } }
src/error.rs+7 −0 modified@@ -125,6 +125,13 @@ impl Error { .into() } + pub(crate) fn invalid_response(message: impl Into<String>) -> Error { + ErrorKind::InvalidResponse { + message: message.into(), + } + .into() + } + /// Construct a generic network timeout error. pub(crate) fn network_timeout() -> Error { ErrorKind::Io(Arc::new(std::io::ErrorKind::TimedOut.into())).into()
src/gridfs.rs+4 −3 modified@@ -11,6 +11,7 @@ use serde_with::skip_serializing_none; use crate::{ bson::{doc, oid::ObjectId, Bson, DateTime, Document, RawBinaryRef}, + checked::Checked, cursor::Cursor, error::{Error, ErrorKind, GridFsErrorKind, GridFsFileIdentifier, Result}, options::{ @@ -83,8 +84,8 @@ impl FilesCollectionDocument { fn n_from_vals(length: u64, chunk_size_bytes: u32) -> u32 { let chunk_size_bytes = chunk_size_bytes as u64; - let n = length / chunk_size_bytes + u64::from(length % chunk_size_bytes != 0); - n as u32 + let n = Checked::new(length) / chunk_size_bytes + u64::from(length % chunk_size_bytes != 0); + n.try_into().unwrap() } /// Returns the expected length of a chunk given its index. @@ -95,7 +96,7 @@ impl FilesCollectionDocument { fn expected_chunk_length_from_vals(length: u64, chunk_size_bytes: u32, n: u32) -> u32 { let remainder = length % (chunk_size_bytes as u64); if n == Self::n_from_vals(length, chunk_size_bytes) - 1 && remainder != 0 { - remainder as u32 + Checked::new(remainder).try_into().unwrap() } else { chunk_size_bytes }
src/gridfs/upload.rs+8 −6 modified@@ -15,6 +15,7 @@ use crate::{ action::Action, bson::{doc, oid::ObjectId, spec::BinarySubtype, Bson, DateTime, Document, RawBinaryRef}, bson_util::get_int, + checked::Checked, client::AsyncDropToken, error::{Error, ErrorKind, GridFsErrorKind, Result}, index::IndexModel, @@ -509,21 +510,22 @@ async fn write_bytes( chunk_size_bytes: u32, files_id: Bson, ) -> Result<(u32, Vec<u8>)> { + let chunk_size_bytes: usize = Checked::new(chunk_size_bytes).try_into()?; bucket.create_indexes().await?; - let mut n = 0; + let mut n = Checked::new(0); let mut chunks = vec![]; - while buffer.len() as u32 - (n * chunk_size_bytes) >= chunk_size_bytes { + while (Checked::new(buffer.len()) - (n * chunk_size_bytes)).get()? >= chunk_size_bytes { let start = n * chunk_size_bytes; let end = (n + 1) * chunk_size_bytes; let chunk = Chunk { id: ObjectId::new(), files_id: files_id.clone(), - n: starting_n + n, + n: starting_n + n.try_into::<u32>()?, data: RawBinaryRef { subtype: BinarySubtype::Generic, - bytes: &buffer[(start as usize)..(end as usize)], + bytes: &buffer[start.get()?..end.get()?], }, }; n += 1; @@ -532,8 +534,8 @@ async fn write_bytes( match bucket.chunks().insert_many(chunks, None).await { Ok(_) => { - buffer.drain(..(n * chunk_size_bytes) as usize); - Ok((n, buffer)) + buffer.drain(..(n * chunk_size_bytes).get()?); + Ok((n.try_into()?, buffer)) } Err(error) => match clean_up_chunks(files_id, bucket.chunks().clone(), Some(error)).await { // clean_up_chunks will always return an error if one is passed in, so this case is
src/hello.rs+7 −1 modified@@ -54,7 +54,13 @@ pub(crate) fn hello_command( if let Some(opts) = awaitable_options { command.insert("topologyVersion", opts.topology_version); - command.insert("maxAwaitTimeMS", opts.max_await_time.as_millis() as i64); + command.insert( + "maxAwaitTimeMS", + opts.max_await_time + .as_millis() + .try_into() + .unwrap_or(i64::MAX), + ); } let mut command = Command::new(command_name, "admin", command);
src/lib.rs+3 −0 modified@@ -271,6 +271,8 @@ #![warn(missing_docs)] #![warn(rustdoc::missing_crate_level_docs)] +#![warn(clippy::cast_possible_truncation)] +#![warn(clippy::cast_possible_wrap)] #![cfg_attr( feature = "cargo-clippy", allow( @@ -296,6 +298,7 @@ pub use ::mongocrypt; pub mod action; mod bson_util; pub mod change_stream; +pub(crate) mod checked; mod client; mod cmap; mod coll;
src/operation/get_more.rs+4 −1 modified@@ -70,7 +70,10 @@ impl<'conn> OperationWithDefaults for GetMore<'conn> { } if let Some(ref max_time) = self.max_time { - body.insert("maxTimeMS", max_time.as_millis() as i32); + body.insert( + "maxTimeMS", + max_time.as_millis().try_into().unwrap_or(i32::MAX), + ); } if let Some(ref comment) = self.comment {
src/operation/insert.rs+12 −15 modified@@ -9,6 +9,7 @@ use serde::Serialize; use crate::{ bson::rawdoc, bson_util, + checked::Checked, cmap::{Command, RawCommandResponse, StreamDescription}, error::{BulkWriteFailure, Error, ErrorKind, Result}, operation::{OperationWithDefaults, Retryability, WriteResponseBody}, @@ -64,14 +65,14 @@ impl<'a, T: Serialize> OperationWithDefaults for Insert<'a, T> { let mut docs = Vec::new(); let mut size = 0; - let max_doc_size = description.max_bson_object_size as u64; + let max_doc_size = Checked::<usize>::try_from(description.max_bson_object_size)?; let max_doc_sequence_size = - description.max_message_size_bytes as u64 - COMMAND_OVERHEAD_SIZE; + Checked::<usize>::try_from(description.max_message_size_bytes)? - COMMAND_OVERHEAD_SIZE; for (i, d) in self .documents .iter() - .take(description.max_write_batch_size as usize) + .take(Checked::new(description.max_write_batch_size).try_into()?) .enumerate() { let mut doc = @@ -92,16 +93,16 @@ impl<'a, T: Serialize> OperationWithDefaults for Insert<'a, T> { bytes.splice(4..4, oid_slice.iter().cloned()); // overwrite old length - let new_length = (bytes.len() as i32).to_le_bytes(); + let new_length = Checked::new(bytes.len()).try_into::<i32>()?.to_le_bytes(); bytes[0..4].copy_from_slice(&new_length); doc = RawDocumentBuf::from_bytes(bytes)?; Bson::ObjectId(oid) } }; - let doc_size = doc.as_bytes().len() as u64; - if doc_size > max_doc_size { + let doc_size = doc.as_bytes().len(); + if doc_size > max_doc_size.get()? { return Err(ErrorKind::InvalidArgument { message: format!( "insert document must be within {} bytes, but document provided is {} \ @@ -116,11 +117,11 @@ impl<'a, T: Serialize> OperationWithDefaults for Insert<'a, T> { // automatic encryption. I.e. if a single document has size larger than 2MiB (but less // than `maxBsonObjectSize`) proceed with automatic encryption. if self.encrypted && i != 0 { - let doc_entry_size = bson_util::array_entry_size_bytes(i, doc.as_bytes().len()); - if size + doc_entry_size >= MAX_ENCRYPTED_WRITE_SIZE { + let doc_entry_size = bson_util::array_entry_size_bytes(i, doc.as_bytes().len())?; + if (Checked::new(size) + doc_entry_size).get()? >= MAX_ENCRYPTED_WRITE_SIZE { break; } - } else if size + doc_size > max_doc_sequence_size { + } else if (Checked::new(size) + doc_size).get()? > max_doc_sequence_size.get()? { break; } @@ -157,16 +158,12 @@ impl<'a, T: Serialize> OperationWithDefaults for Insert<'a, T> { _description: &StreamDescription, ) -> Result<Self::O> { let response: WriteResponseBody = raw_response.body_utf8_lossy()?; + let response_n = Checked::<usize>::try_from(response.n)?; let mut map = HashMap::new(); if self.options.ordered == Some(true) { // in ordered inserts, only the first n were attempted. - for (i, id) in self - .inserted_ids - .iter() - .enumerate() - .take(response.n as usize) - { + for (i, id) in self.inserted_ids.iter().enumerate().take(response_n.get()?) { map.insert(i, id.clone()); } } else {
src/operation.rs+2 −2 modified@@ -71,9 +71,9 @@ const SERVER_4_2_0_WIRE_VERSION: i32 = 8; const SERVER_4_4_0_WIRE_VERSION: i32 = 9; // The maximum number of bytes that may be included in a write payload when auto-encryption is // enabled. -const MAX_ENCRYPTED_WRITE_SIZE: u64 = 2_097_152; +const MAX_ENCRYPTED_WRITE_SIZE: usize = 2_097_152; // The amount of overhead bytes to account for when building a document sequence. -const COMMAND_OVERHEAD_SIZE: u64 = 16_000; +const COMMAND_OVERHEAD_SIZE: usize = 16_000; /// A trait modeling the behavior of a server side operation. ///
src/sdam/description/topology/server_selection.rs+12 −4 modified@@ -291,7 +291,7 @@ impl TopologyDescription { primary: &ServerDescription, max_staleness: Duration, ) { - let max_staleness_ms = max_staleness.as_millis() as i64; + let max_staleness_ms = max_staleness.as_millis().try_into().unwrap_or(i64::MAX); servers.retain(|server| { let server_staleness = self.calculate_secondary_staleness_with_primary(server, primary); @@ -307,7 +307,7 @@ impl TopologyDescription { servers: &mut Vec<&ServerDescription>, max_staleness: Duration, ) { - let max_staleness = max_staleness.as_millis() as i64; + let max_staleness = max_staleness.as_millis().try_into().unwrap_or(i64::MAX); let max_write_date = self .servers .values() @@ -347,7 +347,11 @@ impl TopologyDescription { let secondary_last_update = secondary.last_update_time?.timestamp_millis(); let secondary_last_write = secondary.last_write_date().ok()??.timestamp_millis(); - let heartbeat_frequency = self.heartbeat_frequency().as_millis() as i64; + let heartbeat_frequency = self + .heartbeat_frequency() + .as_millis() + .try_into() + .unwrap_or(i64::MAX); let staleness = (secondary_last_update - secondary_last_write) - (primary_last_update - primary_last_write) @@ -362,7 +366,11 @@ impl TopologyDescription { max_last_write_date: i64, ) -> Option<i64> { let secondary_last_write = secondary.last_write_date().ok()??.timestamp_millis(); - let heartbeat_frequency = self.heartbeat_frequency().as_millis() as i64; + let heartbeat_frequency = self + .heartbeat_frequency() + .as_millis() + .try_into() + .unwrap_or(i64::MAX); let staleness = max_last_write_date - secondary_last_write + heartbeat_frequency; Some(staleness)
src/sdam/description/topology/server_selection/test/in_window.rs+15 −12 modified@@ -198,18 +198,21 @@ async fn load_balancing_test() { counts.sort(); let share_of_selections = (*counts[0] as f64) / ((*counts[0] + *counts[1]) as f64); - assert!( - share_of_selections <= max_share, - "expected no more than {}% of selections, instead got {}%", - (max_share * 100.0) as u32, - (share_of_selections * 100.0) as u32 - ); - assert!( - share_of_selections >= min_share, - "expected at least {}% of selections, instead got {}%", - (min_share * 100.0) as u32, - (share_of_selections * 100.0) as u32 - ); + #[allow(clippy::cast_possible_truncation)] + { + assert!( + share_of_selections <= max_share, + "expected no more than {}% of selections, instead got {}%", + (max_share * 100.0) as u32, + (share_of_selections * 100.0) as u32 + ); + assert!( + share_of_selections >= min_share, + "expected at least {}% of selections, instead got {}%", + (min_share * 100.0) as u32, + (share_of_selections * 100.0) as u32 + ); + } } let mut handler = EventHandler::new();
src/sdam/description/topology/test.rs+1 −0 modified@@ -6,6 +6,7 @@ use std::time::Duration; pub use event::TestSdamEvent; +#[allow(clippy::cast_possible_truncation)] pub(crate) fn f64_ms_as_duration(f: f64) -> Duration { Duration::from_micros((f * 1000.0) as u64) }
src/serde_util.rs+16 −6 modified@@ -18,9 +18,13 @@ pub(crate) mod duration_option_as_int_seconds { serializer: S, ) -> std::result::Result<S::Ok, S::Error> { match val { - Some(duration) if duration.as_secs() > i32::MAX as u64 => { - serializer.serialize_i64(duration.as_secs() as i64) - } + Some(duration) if duration.as_secs() > i32::MAX as u64 => serializer.serialize_i64( + duration + .as_secs() + .try_into() + .map_err(serde::ser::Error::custom)?, + ), + #[allow(clippy::cast_possible_truncation)] Some(duration) => serializer.serialize_i32(duration.as_secs() as i32), None => serializer.serialize_none(), } @@ -42,9 +46,13 @@ pub(crate) fn serialize_duration_option_as_int_millis<S: Serializer>( serializer: S, ) -> std::result::Result<S::Ok, S::Error> { match val { - Some(duration) if duration.as_millis() > i32::MAX as u128 => { - serializer.serialize_i64(duration.as_millis() as i64) - } + Some(duration) if duration.as_millis() > i32::MAX as u128 => serializer.serialize_i64( + duration + .as_millis() + .try_into() + .map_err(serde::ser::Error::custom)?, + ), + #[allow(clippy::cast_possible_truncation)] Some(duration) => serializer.serialize_i32(duration.as_millis() as i32), None => serializer.serialize_none(), } @@ -77,6 +85,7 @@ pub(crate) fn serialize_u32_option_as_batch_size<S: Serializer>( serializer: S, ) -> std::result::Result<S::Ok, S::Error> { match val { + #[allow(clippy::cast_possible_wrap)] Some(val) if *val <= std::i32::MAX as u32 => (doc! { "batchSize": (*val as i32) }) @@ -141,6 +150,7 @@ where } let date_time = match AwsDateTime::deserialize(deserializer)? { + #[allow(clippy::cast_possible_truncation)] AwsDateTime::Double(seconds) => { let millis = seconds * 1000.0; bson::DateTime::from_millis(millis as i64)
src/test.rs+3 −0 modified@@ -1,3 +1,6 @@ +#![allow(clippy::cast_possible_truncation)] +#![allow(clippy::cast_possible_wrap)] + mod atlas_connectivity; mod atlas_planned_maintenance_testing; #[cfg(feature = "aws-auth")]
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-32jf-h775-g29hghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-6382ghsaADVISORY
- github.com/mongodb/mongo-rust-driver/commit/8eac3bc6dc37a6d7667ed6c1a895c224e3ff47e1ghsaWEB
- github.com/mongodb/mongo-rust-driver/commit/a3fe6c84ce6287348b1268f651fdac9fbed66187ghsaWEB
- github.com/mongodb/mongo-rust-driver/pull/1045ghsaWEB
- jira.mongodb.org/browse/RUST-1881ghsaWEB
News mentions
0No linked articles in our index yet.