From baa9ae905777675389843d49732a54cc998f1ec4 Mon Sep 17 00:00:00 2001 From: mzachar Date: Thu, 12 Sep 2024 00:11:29 +0200 Subject: [PATCH] ureq http client with tls support - supports both native-tls and rustls --- Cargo.lock | 494 +++++++++++++++++-- Cargo.toml | 12 +- src/connector/error.rs | 19 +- src/connector/http.rs | 3 +- src/connector/http/client.rs | 18 - src/connector/http/client/connection.rs | 90 ---- src/connector/http/client/error.rs | 149 ------ src/connector/http/client/path.rs | 39 -- src/connector/http/client/request.rs | 18 - src/connector/http/client/response.rs | 6 - src/connector/http/client/response/body.rs | 18 - src/connector/http/client/response/reader.rs | 165 ------- src/connector/http/config.rs | 26 +- src/connector/http/connection.rs | 95 +++- 14 files changed, 590 insertions(+), 562 deletions(-) delete mode 100644 src/connector/http/client.rs delete mode 100644 src/connector/http/client/connection.rs delete mode 100644 src/connector/http/client/error.rs delete mode 100644 src/connector/http/client/path.rs delete mode 100644 src/connector/http/client/request.rs delete mode 100644 src/connector/http/client/response.rs delete mode 100644 src/connector/http/client/response/body.rs delete mode 100644 src/connector/http/client/response/reader.rs diff --git a/Cargo.lock b/Cargo.lock index c3af6421..e13eaea6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "aead" version = "0.5.2" @@ -42,6 +48,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64ct" version = "1.6.0" @@ -89,9 +101,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.101" +version = "1.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac367972e516d45567c7eafc73d24e1c193dcf200a8d94e9db7b3d38b349572d" +checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" +dependencies = [ + "shlex", +] [[package]] name = "ccm" @@ -144,15 +159,40 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + [[package]] name = "crypto-bigint" version = "0.5.5" @@ -236,9 +276,9 @@ dependencies = [ [[package]] name = "der_derive" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fe87ce4529967e0ba1dcf8450bab64d97dfd5010a6256187ffe2e43e6f0e049" +checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" dependencies = [ "proc-macro2", "quote", @@ -326,6 +366,22 @@ dependencies = [ "zeroize", ] +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + [[package]] name = "ff" version = "0.13.0" @@ -344,9 +400,43 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "flagset" -version = "0.4.5" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3ea1ec5f8307826a5b71094dd91fc04d4ae75d5709b20ad351c7fb4815c86ec" + +[[package]] +name = "flate2" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdeb3aa5e95cf9aabc17f060cfa0ced7b83f042390760ca53bf09df9968acaa1" +checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] [[package]] name = "generic-array" @@ -396,6 +486,16 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "inout" version = "0.1.3" @@ -437,9 +537,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.155" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libm" @@ -459,12 +559,50 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "num-bigint-dig" version = "0.8.4" @@ -524,6 +662,60 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "openssl" +version = "0.10.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-src" +version = "300.3.2+3.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a211a18d945ef7e648cc6e0058f4c548ee46aab922ea203e0d30e966ea23647b" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + [[package]] name = "p256" version = "0.13.2" @@ -567,6 +759,12 @@ dependencies = [ "base64ct", ] +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "pkcs1" version = "0.7.5" @@ -602,9 +800,12 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "primeorder" @@ -626,9 +827,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -706,19 +907,41 @@ dependencies = [ [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "schannel" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "sec1" version = "0.7.3" @@ -733,6 +956,29 @@ dependencies = [ "zeroize", ] +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "1.0.23" @@ -741,18 +987,18 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.203" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -761,11 +1007,12 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.118" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -792,6 +1039,12 @@ dependencies = [ "digest", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signature" version = "2.2.0" @@ -844,29 +1097,42 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.68" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", @@ -915,6 +1181,21 @@ dependencies = [ "log", ] +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tls_codec" version = "0.4.1" @@ -942,17 +1223,57 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "ureq" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a" +dependencies = [ + "base64", + "flate2", + "log", + "native-tls", + "once_cell", + "url", +] + +[[package]] +name = "url" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] [[package]] name = "uuid" -version = "1.9.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "serde", ] @@ -965,9 +1286,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" @@ -975,6 +1296,88 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "x509-cert" version = "0.2.5" @@ -1005,6 +1408,7 @@ dependencies = [ "hmac", "k256", "log", + "native-tls", "num-traits", "once_cell", "p256", @@ -1023,11 +1427,33 @@ dependencies = [ "thiserror", "time", "tiny_http", + "ureq", "uuid", "x509-cert", "zeroize", ] +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zeroize" version = "1.8.1" diff --git a/Cargo.toml b/Cargo.toml index c128e9cf..918dd17d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,11 @@ pbkdf2 = { version = "0.12", optional = true, default-features = false, features serde_json = { version = "1", optional = true } rusb = { version = "0.9.4", optional = true } tiny_http = { version = "0.12", optional = true } +ureq = { version = "2.10.1", optional = true, default-features = false, features = ["gzip"] } +native-tls = { version = "0", optional = true } +rustls = { version = "0", default-features = false, features = ["ring", "logging", "std", "tls12"], optional = true } +rustls-pemfile = { version = "2.1.3", optional = true } +rustls-platform-verifier = { version = "0.3.4", optional = true } [dev-dependencies] ed25519-dalek = "2" @@ -61,7 +66,10 @@ x509-cert = { version = "0.2.5", features = ["builder"] } [features] default = ["http", "passwords", "setup"] http-server = ["tiny_http"] -http = [] +http = ["ureq"] +native-tls = ["ureq/native-tls", "dep:native-tls", "_tls"] +native-tls-vendored = ["native-tls", "native-tls/vendored"] +rustls = ["ureq/tls", "dep:rustls", "dep:rustls-pemfile", "dep:rustls-platform-verifier", "_tls"] mockhsm = ["ecdsa/arithmetic", "ed25519-dalek", "p256/ecdsa", "secp256k1"] passwords = ["hmac", "pbkdf2"] secp256k1 = ["k256"] @@ -69,6 +77,8 @@ setup = ["passwords", "serde_json", "uuid/serde"] untested = [] usb = ["rusb"] +_tls = [] + [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] diff --git a/src/connector/error.rs b/src/connector/error.rs index 63acd82d..18ecca9c 100644 --- a/src/connector/error.rs +++ b/src/connector/error.rs @@ -4,9 +4,6 @@ use crate::error::{BoxError, Context}; use std::{fmt, io, num::ParseIntError, str::Utf8Error}; use thiserror::Error; -#[cfg(feature = "http")] -use super::http; - /// `yubihsm-connector` related errors pub type Error = crate::Error; @@ -67,15 +64,13 @@ impl From for Error { } #[cfg(feature = "http")] -impl From for Error { - fn from(err: http::client::Error) -> Error { +impl From for Error { + fn from(err: ureq::Error) -> Error { let kind = match err.kind() { - http::client::ErrorKind::AddrInvalid => ErrorKind::AddrInvalid, - http::client::ErrorKind::IoError => ErrorKind::IoError, - http::client::ErrorKind::ParseError | http::client::ErrorKind::ResponseError => { - ErrorKind::ResponseError - } - http::client::ErrorKind::RequestError => ErrorKind::RequestError, + ureq::ErrorKind::Dns => ErrorKind::AddrInvalid, + ureq::ErrorKind::Io => ErrorKind::IoError, + ureq::ErrorKind::HTTP => ErrorKind::ResponseError, + _ => ErrorKind::RequestError, }; kind.context(err).into() @@ -91,7 +86,7 @@ impl From for Error { rusb::Error::Pipe => format_err!(ErrorKind::UsbError, "lost connection to USB device"), _ => format_err!(ErrorKind::UsbError, "{}", err), } - .into() + .into() } } diff --git a/src/connector/http.rs b/src/connector/http.rs index cbe2f489..bd7ffcd5 100644 --- a/src/connector/http.rs +++ b/src/connector/http.rs @@ -2,8 +2,8 @@ //! //! -pub(super) mod client; mod config; +#[cfg(feature = "http")] mod connection; #[cfg(feature = "http-server")] mod server; @@ -12,6 +12,7 @@ pub use self::config::HttpConfig; #[cfg(feature = "http-server")] pub use self::server::Server; +#[cfg(feature = "http")] use self::connection::HttpConnection; use crate::connector::{self, Connectable, Connection}; diff --git a/src/connector/http/client.rs b/src/connector/http/client.rs deleted file mode 100644 index 9bda2d4a..00000000 --- a/src/connector/http/client.rs +++ /dev/null @@ -1,18 +0,0 @@ -//! Minimalist HTTP client -// TODO(tarcieri): replace this with e.g. `ureq`? - -#[macro_use] -pub mod error; - -pub mod connection; -pub mod path; -pub mod request; -pub mod response; - -pub use self::{connection::*, error::*}; - -/// HTTP version. -pub const HTTP_VERSION: &str = "HTTP/1.1"; - -/// `User-Agent` string. -pub const USER_AGENT: &str = concat!("yubihsm.rs ", env!("CARGO_PKG_VERSION")); diff --git a/src/connector/http/client/connection.rs b/src/connector/http/client/connection.rs deleted file mode 100644 index 32622dab..00000000 --- a/src/connector/http/client/connection.rs +++ /dev/null @@ -1,90 +0,0 @@ -//! Connections to HTTP servers - -use std::{ - fmt::Write as FmtWrite, - io::Write, - net::{TcpStream, ToSocketAddrs}, - ops::DerefMut, - string::String, - sync::Mutex, - time::Duration, - vec::Vec, -}; - -use super::{error::Error, path::PathBuf, request, response, HTTP_VERSION, USER_AGENT}; - -/// Default timeout in milliseconds (20 seconds) -const DEFAULT_TIMEOUT_MS: u64 = 20000; - -/// Options when building a `Connection` -pub struct ConnectionOptions { - timeout: Duration, -} - -impl Default for ConnectionOptions { - fn default() -> Self { - Self { - timeout: Duration::from_millis(DEFAULT_TIMEOUT_MS), - } - } -} - -/// HTTP connection to a remote host -pub struct Connection { - /// Host header to send in HTTP requests - host: String, - - /// Open TCP socket to remote host - socket: Mutex, -} - -impl Connection { - /// Create a new connection to an HTTP server - pub fn open(addr: &str, port: u16, opts: &ConnectionOptions) -> Result { - let host = format!("{addr}:{port}"); - - let socketaddr = &host.to_socket_addrs()?.next().ok_or_else(|| { - err!( - AddrInvalid, - "couldn't resolve DNS for {}", - host.split(':').next().unwrap() - ) - })?; - - // TODO: better timeout handling? - let socket = TcpStream::connect_timeout(socketaddr, opts.timeout)?; - socket.set_read_timeout(Some(opts.timeout))?; - socket.set_write_timeout(Some(opts.timeout))?; - - Ok(Self { - host, - socket: Mutex::new(socket), - }) - } - - /// Make an HTTP POST request to the given path - pub fn post>( - &self, - into_path: P, - body: &request::Body, - ) -> Result { - let path = into_path.into(); - let mut headers = String::new(); - - writeln!(headers, "POST {path} {HTTP_VERSION}\r")?; - writeln!(headers, "Host: {}\r", self.host)?; - writeln!(headers, "User-Agent: {USER_AGENT}\r")?; - writeln!(headers, "Content-Length: {}\r", body.0.len())?; - writeln!(headers, "\r")?; - - // Make a Nagle-friendly request by combining headers and body - let mut request: Vec = headers.into(); - request.extend_from_slice(body.0.as_slice()); - - let mut socket = self.socket.lock().unwrap(); - socket.write_all(&request)?; - - let response_body = response::Reader::new(socket.deref_mut())?.into_body(); - Ok(response_body) - } -} diff --git a/src/connector/http/client/error.rs b/src/connector/http/client/error.rs deleted file mode 100644 index 44a3169d..00000000 --- a/src/connector/http/client/error.rs +++ /dev/null @@ -1,149 +0,0 @@ -//! Error types - -#![allow(unused_macros)] - -use std::{fmt, num::ParseIntError, str::Utf8Error}; -use std::{ - io, - string::{FromUtf8Error, String, ToString}, -}; - -/// Error type -#[derive(Debug)] -pub struct Error { - /// Error context and kind - kind: ErrorKind, - - /// Optional description - description: Option, -} - -impl Error { - /// Create a new error object with an optional error message - #[allow(unused_variables)] - pub fn new(kind: ErrorKind, description: Option<&str>) -> Self { - let mut err = Self::from(kind); - err.description = description.map(|desc| desc.into()); - err - } - - /// Obtain the inner `ErrorKind` for this `Error` - pub fn kind(&self) -> ErrorKind { - self.kind - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.kind.fmt(f) - } -} - -impl std::error::Error for Error {} - -impl From for Error { - fn from(kind: ErrorKind) -> Error { - Error { - kind, - description: None, - } - } -} - -/// Kinds of errors -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum ErrorKind { - /// Invalid address - AddrInvalid, - - /// I/O operation failed - IoError, - - /// Parsing data failed - ParseError, - - /// Request failed - RequestError, - - /// Error reading response - ResponseError, -} - -impl fmt::Display for ErrorKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let description = match self { - ErrorKind::AddrInvalid => "address invalid", - ErrorKind::IoError => "I/O error", - ErrorKind::ParseError => "parse error", - ErrorKind::RequestError => "request error", - ErrorKind::ResponseError => "error reading response", - }; - - write!(f, "{description}") - } -} - -/// Create a new error (of a given enum variant) with a formatted message -macro_rules! err { - ($variant:ident, $msg:expr) => { - crate::connector::http::client::error::Error::new( - crate::connector::http::client::error::ErrorKind::$variant, - Some($msg) - ) - }; - ($variant:ident, $fmt:expr, $($arg:tt)+) => { - err!($variant, &format!($fmt, $($arg)+)) - }; -} - -/// Create and return an error with a formatted message -macro_rules! fail { - ($kind:ident, $msg:expr) => { - return Err(err!($kind, $msg).into()) - }; - ($kind:ident, $fmt:expr, $($arg:tt)+) => { - fail!($kind, &format!($fmt, $($arg)+)) - }; -} - -/// Assert a condition is true, returning an error type with a formatted message if not -macro_rules! ensure { - ($condition: expr, $variant:ident, $msg:expr) => { - if !($condition) { - return Err(err!($variant, $msg).into()); - } - }; - ($condition: expr, $variant:ident, $fmt:expr, $($arg:tt)+) => { - ensure!($variant, $fmt, $($arg)+); - }; -} - -impl From for Error { - fn from(err: ParseIntError) -> Self { - err!(ParseError, &err.to_string()) - } -} - -impl From for Error { - fn from(err: FromUtf8Error) -> Self { - err!(ParseError, &err.to_string()) - } -} - -impl From for Error { - fn from(err: Utf8Error) -> Self { - err!(ParseError, &err.to_string()) - } -} - -impl From for Error { - fn from(err: fmt::Error) -> Self { - err!(RequestError, &err.to_string()) - } -} - -impl From for Error { - fn from(err: io::Error) -> Self { - err!(IoError, &err.to_string()) - } -} diff --git a/src/connector/http/client/path.rs b/src/connector/http/client/path.rs deleted file mode 100644 index 995ee550..00000000 --- a/src/connector/http/client/path.rs +++ /dev/null @@ -1,39 +0,0 @@ -//! Remote paths on HTTP servers - -use super::error::Error; -use std::{ - fmt::{self, Display}, - str::FromStr, -}; - -/// Paths to HTTP resources (owned buffer) -// TODO: corresponding borrowed `Path` type -pub struct PathBuf(String); - -impl FromStr for PathBuf { - type Err = Error; - - /// Create a path from the given string - fn from_str(path: &str) -> Result { - // TODO: validate path - Ok(PathBuf(path.to_owned())) - } -} - -impl AsRef for PathBuf { - fn as_ref(&self) -> &str { - self.0.as_ref() - } -} - -impl Display for PathBuf { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -impl<'a> From<&'a str> for PathBuf { - fn from(path: &str) -> Self { - Self::from_str(path).unwrap() - } -} diff --git a/src/connector/http/client/request.rs b/src/connector/http/client/request.rs deleted file mode 100644 index 65a3b7a6..00000000 --- a/src/connector/http/client/request.rs +++ /dev/null @@ -1,18 +0,0 @@ -//! HTTP request types - -/// Request bodies -#[derive(Debug, Default)] -pub struct Body(pub(crate) Vec); - -impl Body { - /// Create a new `Body` from the given byte slice - pub fn new(bytes: &[u8]) -> Body { - Body(bytes.into()) - } -} - -impl From> for Body { - fn from(bytes: Vec) -> Body { - Body(bytes) - } -} diff --git a/src/connector/http/client/response.rs b/src/connector/http/client/response.rs deleted file mode 100644 index 3905ecf4..00000000 --- a/src/connector/http/client/response.rs +++ /dev/null @@ -1,6 +0,0 @@ -//! HTTP response handling - -mod body; -mod reader; - -pub use self::{body::Body, reader::Reader}; diff --git a/src/connector/http/client/response/body.rs b/src/connector/http/client/response/body.rs deleted file mode 100644 index 129ebe5b..00000000 --- a/src/connector/http/client/response/body.rs +++ /dev/null @@ -1,18 +0,0 @@ -//! Response body types. - -/// Response body -#[derive(Debug)] -pub struct Body(pub(super) Vec); - -impl Body { - /// Buffer the response body into a `Vec` and return it - pub fn into_vec(self) -> Vec { - self.0 - } -} - -impl From for Vec { - fn from(body: Body) -> Vec { - body.into_vec() - } -} diff --git a/src/connector/http/client/response/reader.rs b/src/connector/http/client/response/reader.rs deleted file mode 100644 index 44b0d3c9..00000000 --- a/src/connector/http/client/response/reader.rs +++ /dev/null @@ -1,165 +0,0 @@ -//! Read HTTP responses from an `io::Read` - -#![allow(clippy::manual_strip)] - -use super::Body; -use crate::connector::http::client::Error; -use std::{io::Read, str, vec::Vec}; - -const TRANSFER_ENCODING_HEADER: &str = "Transfer-Encoding: "; -const HEADER_DELIMITER: &[u8] = b"\r\n\r\n"; -const HTTP_SUCCESS_STATUS: &str = "HTTP/1.1 200 OK"; -const CONTENT_LENGTH_HEADER: &str = "Content-Length: "; - -/// Maximum response size we can parse. -// TODO: we shouldn't have a max, or at least one this small -const MAX_RESPONSE_SIZE: usize = 65536; - -/// Read HTTP responses from the server -pub struct Reader { - /// Internal buffer - buffer: Vec, - - /// Position within the response - pos: usize, - - /// Offset into the body we've ready so far - body_offset: Option, - - /// Total length of the response content - content_length: usize, -} - -impl Reader { - /// Create a new `response::Reader` that consumes a response body from a socket - #[allow(clippy::new_ret_no_self)] - pub(crate) fn new(readable: &mut dyn Read) -> Result { - // TODO: better buffering - let mut buffer = Self { - buffer: vec![0u8; MAX_RESPONSE_SIZE], - pos: 0, - body_offset: None, - content_length: 0, - }; - - buffer.read_headers(readable)?; - buffer.read_body(readable)?; - - Ok(buffer) - } - - /// Convert this `response::Reader` into a `response::Body` - pub(crate) fn into_body(self) -> Body { - let body_offset = self - .body_offset - .expect("we should've already read the body"); - - Body(Vec::from(&self.buffer[body_offset..self.pos])) - } - - /// Fill the internal buffer with data from the socket - fn fill_buffer(&mut self, readable: &mut dyn Read) -> Result { - let nbytes = readable.read(self.buffer.as_mut())?; - self.pos += nbytes; - - // See: https://doc.rust-lang.org/src/std/io/mod.rs.html#571 - // On Linux, read method will call the recv syscall for a TcpStream, - // where returning zero indicates the connection was shut down correctly - if nbytes == 0 { - fail!( - ResponseError, - "read {} bytes, the remote connection was likely shutdown", - nbytes - ); - } - - Ok(nbytes) - } - - /// Read the response headers - fn read_headers(&mut self, readable: &mut dyn Read) -> Result<(), Error> { - assert!(self.body_offset.is_none(), "already read headers!"); - - loop { - self.fill_buffer(readable)?; - - // Scan for the header delimiter - // TODO: real parser - let mut offset = 0; - while self.buffer[offset..].len() > HEADER_DELIMITER.len() { - if self.buffer[offset..].starts_with(HEADER_DELIMITER) { - self.body_offset = Some(offset + HEADER_DELIMITER.len()); - break; - } else { - offset += 1; - } - } - - if self.body_offset.is_some() { - break; - } else if self.pos + 1 >= MAX_RESPONSE_SIZE { - fail!( - ResponseError, - "exceeded {}-byte response limit reading headers", - MAX_RESPONSE_SIZE - ); - } - } - - self.parse_headers() - } - - /// Parse the response headers - fn parse_headers(&mut self) -> Result<(), Error> { - let body_offset = self.body_offset.unwrap(); - let header_str = str::from_utf8(&self.buffer[..body_offset])?; - let mut header_iter = header_str.split("\r\n"); - - match header_iter.next() { - Some(HTTP_SUCCESS_STATUS) => (), - Some(status) => fail!( - ResponseError, - "unexpected HTTP response status: \"{}\"", - status - ), - None => fail!(ResponseError, "HTTP response status line missing!"), - } - - for header in header_iter { - if header.starts_with(CONTENT_LENGTH_HEADER) { - let content_length: usize = header[CONTENT_LENGTH_HEADER.len()..].parse()?; - - if MAX_RESPONSE_SIZE - body_offset < content_length { - fail!( - ResponseError, - "response body length too large for buffer ({} bytes)", - content_length - ); - } - - self.content_length = content_length; - } else if header.starts_with(TRANSFER_ENCODING_HEADER) { - let transfer_encoding = &header[TRANSFER_ENCODING_HEADER.len()..]; - fail!( - ResponseError, - "connection sent unsupported transfer encoding: {}", - transfer_encoding - ); - } - } - - Ok(()) - } - - /// Read the response body into the internal buffer - fn read_body(&mut self, readable: &mut dyn Read) -> Result<(), Error> { - let body_end = - self.content_length + self.body_offset.expect("not ready to read the body yet"); - - while self.pos < body_end { - self.fill_buffer(readable)?; - } - - Ok(()) - } -} diff --git a/src/connector/http/config.rs b/src/connector/http/config.rs index 93764024..ff55f3de 100644 --- a/src/connector/http/config.rs +++ b/src/connector/http/config.rs @@ -2,6 +2,9 @@ use serde::{Deserialize, Serialize}; use std::fmt::{self, Display}; +#[cfg(feature = "_tls")] +use std::path::PathBuf; + /// Default timeouts for reading and writing (5 seconds) pub const DEFAULT_TIMEOUT_MILLIS: u64 = 5000; @@ -15,6 +18,14 @@ pub struct HttpConfig { /// Port `yubihsm-connector` process is listening on pub port: u16, + /// Use https if true + #[cfg(feature = "_tls")] + pub tls: bool, + + /// CA certificate to validate the server certificate + #[cfg(feature = "_tls")] + pub cacert: Option, + /// Timeout for connecting, reading, and writing in milliseconds pub timeout_ms: u64, } @@ -28,6 +39,12 @@ impl Default for HttpConfig { // Default `yubihsm-connector` port port: 12345, + #[cfg(feature = "_tls")] + tls: false, + + #[cfg(feature = "_tls")] + cacert: None, + // 5 seconds timeout_ms: DEFAULT_TIMEOUT_MILLIS, } @@ -36,7 +53,14 @@ impl Default for HttpConfig { impl Display for HttpConfig { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // TODO: HTTPS support + #[cfg(feature = "_tls")] + if self.tls { + write!(f, "https://{}:{}", self.addr, self.port) + } else { + write!(f, "http://{}:{}", self.addr, self.port) + } + + #[cfg(not(feature = "_tls"))] write!(f, "http://{}:{}", self.addr, self.port) } } diff --git a/src/connector/http/connection.rs b/src/connector/http/connection.rs index 0538e6ac..3ee8e98c 100644 --- a/src/connector/http/connection.rs +++ b/src/connector/http/connection.rs @@ -1,9 +1,21 @@ //! Persistent HTTP connection to `yubihsm-connector` -use super::{client, config::HttpConfig}; +use std::io::Read; +use std::time::Duration; +#[cfg(feature = "_tls")] +use std::sync::Arc; +#[cfg(feature = "native-tls")] +use native_tls::{Certificate, TlsConnector}; +#[cfg(feature = "rustls")] +use rustls::ClientConfig; +use ureq::{Agent, AgentBuilder}; +use super::{config::HttpConfig}; use crate::connector::{self, Connection}; use uuid::Uuid; +const MAX_BODY_SIZE: u64 = 1024 ^ 3;/*1MB*/ +const USER_AGENT: &str = concat!("yubihsm.rs ", env!("CARGO_PKG_VERSION")); + /// Connection to YubiHSM via HTTP requests to `yubihsm-connector`. /// /// The `yubihsm-connector` service is a small HTTP(S) service which exposes a @@ -15,29 +27,50 @@ use uuid::Uuid; /// pub struct HttpConnection { /// HTTP connection - connection: client::Connection, + agent: Agent, + + base_url: String, } impl HttpConnection { /// Open a connection to a `yubihsm-connector` service pub(crate) fn open(config: &HttpConfig) -> Result { - let connection = client::Connection::open(&config.addr, config.port, &Default::default())?; + let mut agent = AgentBuilder::new() + .timeout(Duration::from_millis(config.timeout_ms)) + .user_agent(USER_AGENT); + + #[cfg(feature = "_tls")] + if config.tls { + #[cfg(feature = "native-tls")] + agent = agent.tls_connector(Arc::new(build_tls_connector(config)?)); + #[cfg(feature = "rustls")] + agent = agent.tls_config(Arc::new(build_tls_config(config)?)); + } - Ok(HttpConnection { connection }) + Ok(HttpConnection { + agent: agent.build(), + base_url: format!("{config}"), + }) } /// Make an HTTP POST request to a `yubihsm-connector` service pub(super) fn post( &self, path: &str, - _uuid: Uuid, + uuid: Uuid, body: &[u8], ) -> Result, connector::Error> { - // TODO: send UUID as `X-Request-ID` header, zero copy body creation - Ok(self - .connection - .post(path, &client::request::Body::new(body))? - .into_vec()) + let response = self.agent.post(&format!("{}{}", self.base_url, path)) + .set("X-Request-ID", &uuid.to_string()) + .send_bytes(body)?; + + let mut data = response.header("Content-Length") + .and_then(|len| len.parse::().ok()) + .map(|len| Vec::with_capacity(len)) + .unwrap_or(Vec::new()); + + response.into_reader().take(MAX_BODY_SIZE).read_to_end(&mut data)?; + Ok(data) } } @@ -52,3 +85,45 @@ impl Connection for HttpConnection { .map(Into::into) } } + +#[cfg(feature = "native-tls")] +fn build_tls_connector(config: &HttpConfig) -> Result { + use std::fs; + use crate::connector::ErrorKind; + + let mut builder = TlsConnector::builder(); + + if let Some(path) = config.cacert.as_ref() { + let data = fs::read(path)?; + let cert = Certificate::from_pem(&data) + .map_err(|e| ErrorKind::IoError.context(e))?; + + builder.add_root_certificate(cert); + } + + builder.build() + .map_err(|e| ErrorKind::IoError.context(e).into()) +} + +#[cfg(feature = "rustls")] +pub fn build_tls_config(config: &HttpConfig) -> Result { + use std::fs::File; + use std::io::BufReader; + use rustls::RootCertStore; + use crate::connector::ErrorKind; + + match config.cacert.as_ref() { + None => Ok(rustls_platform_verifier::tls_config()), + Some(cert_path) => { + let mut root_store = RootCertStore::empty(); + let mut reader = BufReader::new(File::open(cert_path)?); + for cert in rustls_pemfile::certs(&mut reader) { + root_store.add(cert?) + .map_err(|e| ErrorKind::IoError.context(e))?; + } + Ok(ClientConfig::builder() + .with_root_certificates(root_store) + .with_no_client_auth()) + } + } +}