From 8155604c2a46181ec3e6c382e658a8b2d20d85fd Mon Sep 17 00:00:00 2001 From: Denis Molokanov Date: Mon, 26 Apr 2021 17:30:49 -0700 Subject: [PATCH] upgrade api-proxy module to tokio1 (#4879) Upgrades api-proxy and dependecies to tokio1 ecosystem --- edge-modules/api-proxy-module/Cargo.lock | 749 +++------- edge-modules/api-proxy-module/Cargo.toml | 21 +- .../rust-sdk/azure-iot-mqtt/Cargo.lock | 1222 +++++++++++++++++ .../rust-sdk/azure-iot-mqtt/Cargo.toml | 40 +- .../rust-sdk/azure-iot-mqtt/README.md | 2 +- .../rust-sdk/azure-iot-mqtt/src/io.rs | 919 +++++++------ .../azure-iot-mqtt/src/iotedge_client.rs | 658 +++++---- .../azure-iot-mqtt/src/twin_state/desired.rs | 364 ++--- .../azure-iot-mqtt/src/twin_state/reported.rs | 780 ++++++----- .../rust-sdk/hyper-uds/Cargo.toml | 13 - .../rust-sdk/hyper-uds/LICENSE | 21 - .../rust-sdk/hyper-uds/README.md | 8 - .../rust-sdk/hyper-uds/src/lib.rs | 322 ----- edge-modules/api-proxy-module/src/lib.rs | 2 +- edge-modules/api-proxy-module/src/main.rs | 19 +- .../src/monitors/certs_monitor.rs | 26 +- .../src/monitors/config_monitor.rs | 25 +- .../src/monitors/shutdown_handle.rs | 2 +- .../src/monitors/token_client.rs | 6 +- .../src/monitors/token_manager.rs | 2 +- edge-modules/api-proxy-module/src/shutdown.rs | 21 + .../api-proxy-module/src/signals/mod.rs | 1 - .../api-proxy-module/src/signals/shutdown.rs | 18 - 23 files changed, 3003 insertions(+), 2238 deletions(-) create mode 100644 edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/Cargo.lock delete mode 100644 edge-modules/api-proxy-module/rust-sdk/hyper-uds/Cargo.toml delete mode 100644 edge-modules/api-proxy-module/rust-sdk/hyper-uds/LICENSE delete mode 100644 edge-modules/api-proxy-module/rust-sdk/hyper-uds/README.md delete mode 100644 edge-modules/api-proxy-module/rust-sdk/hyper-uds/src/lib.rs create mode 100644 edge-modules/api-proxy-module/src/shutdown.rs delete mode 100644 edge-modules/api-proxy-module/src/signals/mod.rs delete mode 100644 edge-modules/api-proxy-module/src/signals/shutdown.rs diff --git a/edge-modules/api-proxy-module/Cargo.lock b/edge-modules/api-proxy-module/Cargo.lock index a273dd1ed2e..3ed62bd69ed 100644 --- a/edge-modules/api-proxy-module/Cargo.lock +++ b/edge-modules/api-proxy-module/Cargo.lock @@ -11,9 +11,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.37" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee67c11feeac938fae061b232e38e0b6d94f97a9df10e6271319325ac4c56a86" +checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" [[package]] name = "api-proxy-module" @@ -21,12 +21,11 @@ version = "0.1.0" dependencies = [ "anyhow", "azure-iot-mqtt", - "base64 0.11.0", + "base64", "chrono", "edgelet-client", "env_logger", "envsubst", - "futures", "futures-util", "lazy_static", "log", @@ -34,20 +33,19 @@ dependencies = [ "md5", "mockito", "mqtt3", - "percent-encoding 1.0.1", + "percent-encoding", "regex", "serde_json", - "tokio 0.2.24", + "tokio", "url", ] [[package]] name = "assert-json-diff" -version = "1.1.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4259cbe96513d2f1073027a259fc2ca917feb3026a5a8d984e3628e490255cc0" +checksum = "50f1c3703dd33532d7f0ca049168930e9099ecac238e23cf932f3a69c42f06da" dependencies = [ - "extend", "serde", "serde_json", ] @@ -71,7 +69,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi", "libc", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -84,38 +82,32 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" name = "azure-iot-mqtt" version = "0.1.0" dependencies = [ - "base64 0.11.0", - "bytes 0.5.6", + "base64", + "bytes 1.0.1", "futures-channel", "futures-core", "futures-util", "hmac", "http", - "hyper 0.13.9", - "hyper-uds", + "hyper", + "hyperlocal", "lazy_static", "log", "mqtt3", "native-tls", - "percent-encoding 2.1.0", + "percent-encoding", "regex", "serde", "serde_derive", "serde_json", "sha2", - "tokio 0.2.24", + "tokio", "tokio-io-timeout", - "tokio-tls", + "tokio-native-tls", "tungstenite", "url", ] -[[package]] -name = "base64" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" - [[package]] name = "base64" version = "0.13.0" @@ -130,31 +122,13 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "block-buffer" -version = "0.7.3" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "block-padding", - "byte-tools", - "byteorder", "generic-array", ] -[[package]] -name = "block-padding" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -dependencies = [ - "byte-tools", -] - -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" - [[package]] name = "byteorder" version = "1.3.4" @@ -201,18 +175,18 @@ dependencies = [ "num-integer", "num-traits", "time", - "winapi 0.3.9", + "winapi", ] [[package]] name = "colored" -version = "1.9.3" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" +checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" dependencies = [ "atty", "lazy_static", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -231,11 +205,17 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" +[[package]] +name = "cpuid-bool" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" + [[package]] name = "crypto-mac" -version = "0.7.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" +checksum = "4857fd85a0c34b3c3297875b747c1e02e06b6a0ea32dd892d8192b9ce0813ea6" dependencies = [ "generic-array", "subtle", @@ -249,46 +229,40 @@ checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" [[package]] name = "digest" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ "generic-array", ] -[[package]] -name = "dtoa" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d7ed2934d741c6b37e33e3832298e8850b53fd2d2bea03873375596c7cea4e" - [[package]] name = "edgelet-client" version = "0.1.0" dependencies = [ "async-trait", - "base64 0.13.0", + "base64", "bytes 1.0.1", "chrono", "futures-util", "http", - "hyper 0.14.5", + "hyper", "hyperlocal", "openssl", - "percent-encoding 1.0.1", + "percent-encoding", "serde", "serde_json", "thiserror", - "tokio 1.5.0", + "tokio", "tower-service", "url", ] [[package]] name = "env_logger" -version = "0.7.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +checksum = "17392a012ea30ef05a610aa97dfb49496e71c9f676b27879922ea5bdf60d9d3f" dependencies = [ "atty", "humantime", @@ -306,24 +280,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "extend" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f47da3a72ec598d9c8937a7ebca8962a5c7a1f28444e38c2b33c771ba3f55f05" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" - [[package]] name = "fnv" version = "1.0.7" @@ -352,45 +308,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" dependencies = [ "matches", - "percent-encoding 2.1.0", -] - -[[package]] -name = "fuchsia-zircon" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -dependencies = [ - "bitflags", - "fuchsia-zircon-sys", -] - -[[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" - -[[package]] -name = "futures" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b3b0c040a1fe6529d30b3c5944b280c7f0dcb2930d2c3062bca967b602583d0" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", + "percent-encoding", ] [[package]] name = "futures-channel" -version = "0.3.8" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b7109687aa4e177ef6fe84553af6280ef2778bdb7783ba44c9dc3399110fe64" +checksum = "ce79c6a52a299137a6013061e0cf0e688fce5d7f1bc60125f520912fdb29ec25" dependencies = [ "futures-core", "futures-sink", @@ -398,32 +323,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "847ce131b72ffb13b6109a221da9ad97a64cbe48feb1028356b836b47b8f1748" - -[[package]] -name = "futures-executor" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4caa2b2b68b880003057c1dd49f1ed937e38f22fcf6c212188a121f08cf40a65" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.8" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611834ce18aaa1bd13c4b374f5d653e1027cf99b6b502584ff8c9a64413b30bb" +checksum = "098cd1c6dda6ca01650f1a37a794245eb73181d0d4d4e955e2f3c37db7af1815" [[package]] name = "futures-macro" -version = "0.3.8" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77408a692f1f97bcc61dc001d752e00643408fbc922e4d634c655df50d595556" +checksum = "668c6733a182cd7deb4f1de7ba3bf2120823835b3bcfbeacf7d2c4a773c1bb8b" dependencies = [ "proc-macro-hack", "proc-macro2", @@ -433,33 +341,27 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.8" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f878195a49cee50e006b02b93cf7e0a95a38ac7b776b4c4d9cc1207cd20fcb3d" +checksum = "5c5629433c555de3d82861a7a4e3794a4c40040390907cfbfd7143a92a426c23" [[package]] name = "futures-task" -version = "0.3.8" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c554eb5bf48b2426c4771ab68c6b14468b6e76cc90996f528c3338d761a4d0d" -dependencies = [ - "once_cell", -] +checksum = "ba7aa51095076f3ba6d9a1f702f74bd05ec65f555d70d2033d55ba8d69f581bc" [[package]] name = "futures-util" -version = "0.3.8" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d304cff4a7b99cfb7986f7d43fbe93d175e72e704a8860787cc95e9ffd85cbd2" +checksum = "3c144ad54d60f23927f0a6b6d816e4271278b64f005ad65e4e35291d2de9c025" dependencies = [ - "futures-channel", "futures-core", - "futures-io", "futures-macro", "futures-sink", "futures-task", - "memchr", - "pin-project 1.0.3", + "pin-project-lite", "pin-utils", "proc-macro-hack", "proc-macro-nested", @@ -468,11 +370,12 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.12.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" dependencies = [ "typenum", + "version_check", ] [[package]] @@ -487,31 +390,16 @@ dependencies = [ ] [[package]] -name = "h2" -version = "0.2.7" +name = "getrandom" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535" +checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" dependencies = [ - "bytes 0.5.6", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio 0.2.24", - "tokio-util 0.3.1", - "tracing", - "tracing-futures", + "cfg-if 1.0.0", + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", ] -[[package]] -name = "hashbrown" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" - [[package]] name = "hermit-abi" version = "0.1.17" @@ -523,15 +411,15 @@ dependencies = [ [[package]] name = "hex" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hmac" -version = "0.7.1" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" +checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" dependencies = [ "crypto-mac", "digest", @@ -548,16 +436,6 @@ dependencies = [ "itoa", ] -[[package]] -name = "http-body" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" -dependencies = [ - "bytes 0.5.6", - "http", -] - [[package]] name = "http-body" version = "0.4.1" @@ -566,7 +444,7 @@ checksum = "5dfb77c123b4e2f72a2069aeae0b4b4949cc7e966df277813fc16347e7549737" dependencies = [ "bytes 1.0.1", "http", - "pin-project-lite 0.2.1", + "pin-project-lite", ] [[package]] @@ -583,36 +461,9 @@ checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" [[package]] name = "humantime" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error", -] - -[[package]] -name = "hyper" -version = "0.13.9" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ad767baac13b44d4529fcf58ba2cd0995e36e7b435bc5b039de6f47e880dbf" -dependencies = [ - "bytes 0.5.6", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body 0.3.1", - "httparse", - "httpdate", - "itoa", - "pin-project 1.0.3", - "socket2 0.3.19", - "tokio 0.2.24", - "tower-service", - "tracing", - "want", -] +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" @@ -625,29 +476,18 @@ dependencies = [ "futures-core", "futures-util", "http", - "http-body 0.4.1", + "http-body", "httparse", "httpdate", "itoa", - "pin-project 1.0.3", + "pin-project", "socket2 0.4.0", - "tokio 1.5.0", + "tokio", "tower-service", "tracing", "want", ] -[[package]] -name = "hyper-uds" -version = "0.1.0" -dependencies = [ - "bytes 0.5.6", - "futures", - "hex", - "hyper 0.13.9", - "tokio 0.2.24", -] - [[package]] name = "hyperlocal" version = "0.8.0" @@ -656,9 +496,9 @@ checksum = "0fafdf7b2b2de7c9784f76e02c0935e65a8117ec3b768644379983ab333ac98c" dependencies = [ "futures-util", "hex", - "hyper 0.14.5", - "pin-project 1.0.3", - "tokio 1.5.0", + "hyper", + "pin-project", + "tokio", ] [[package]] @@ -672,32 +512,13 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "indexmap" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" -dependencies = [ - "autocfg", - "hashbrown", -] - [[package]] name = "input_buffer" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19a8a95243d5a0398cae618ec29477c6e3cb631152be5c19481f80bc71559754" -dependencies = [ - "bytes 0.5.6", -] - -[[package]] -name = "iovec" -version = "0.1.4" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +checksum = "f97967975f448f1a7ddb12b0bc41069d09ed6a1c161a92687e057325db35d413" dependencies = [ - "libc", + "bytes 1.0.1", ] [[package]] @@ -706,16 +527,6 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -730,9 +541,9 @@ checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" [[package]] name = "log" -version = "0.4.12" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3aeeb5dad71cdfb031ff8899db3e3e2bdcbc5abe7ad48e58857e42488dc4aa" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ "cfg-if 1.0.0", ] @@ -755,25 +566,6 @@ version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" -[[package]] -name = "mio" -version = "0.6.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" -dependencies = [ - "cfg-if 0.1.10", - "fuchsia-zircon", - "fuchsia-zircon-sys", - "iovec", - "kernel32-sys", - "libc", - "log", - "miow 0.2.2", - "net2", - "slab", - "winapi 0.2.8", -] - [[package]] name = "mio" version = "0.7.11" @@ -782,44 +574,9 @@ checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956" dependencies = [ "libc", "log", - "miow 0.3.6", + "miow", "ntapi", - "winapi 0.3.9", -] - -[[package]] -name = "mio-named-pipes" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656" -dependencies = [ - "log", - "mio 0.6.23", - "miow 0.3.6", - "winapi 0.3.9", -] - -[[package]] -name = "mio-uds" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" -dependencies = [ - "iovec", - "libc", - "mio 0.6.23", -] - -[[package]] -name = "miow" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" -dependencies = [ - "kernel32-sys", - "net2", - "winapi 0.2.8", - "ws2_32-sys", + "winapi", ] [[package]] @@ -829,14 +586,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" dependencies = [ "socket2 0.3.19", - "winapi 0.3.9", + "winapi", ] [[package]] name = "mockito" -version = "0.25.3" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3ae325bcceb48a24302ac57e1055f9173f5fd53be535603ea0ed41dea92db5" +checksum = "d10030163d67f681db11810bc486df3149e6d91c8b4f3f96fa8b62b546c2cef8" dependencies = [ "assert-json-diff", "colored", @@ -844,7 +601,7 @@ dependencies = [ "httparse", "lazy_static", "log", - "rand", + "rand 0.8.3", "regex", "serde_json", "serde_urlencoded", @@ -853,16 +610,15 @@ dependencies = [ [[package]] name = "mqtt3" version = "0.1.0" -source = "git+https://github.com/Azure/iotedge?rev=4cd508#4cd508994b86d01f7fd673f69a1c1801377765a6" dependencies = [ - "bytes 0.5.6", + "bytes 1.0.1", "futures-channel", "futures-core", "futures-sink", "futures-util", "log", - "tokio 0.2.24", - "tokio-util 0.2.0", + "tokio", + "tokio-util", ] [[package]] @@ -883,24 +639,13 @@ dependencies = [ "tempfile", ] -[[package]] -name = "net2" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" -dependencies = [ - "cfg-if 0.1.10", - "libc", - "winapi 0.3.9", -] - [[package]] name = "ntapi" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" dependencies = [ - "winapi 0.3.9", + "winapi", ] [[package]] @@ -940,9 +685,9 @@ checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" [[package]] name = "opaque-debug" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" @@ -977,45 +722,19 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "percent-encoding" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" - [[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 = "0.4.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffbc8e94b38ea3d2d8ba92aea2983b503cd75d0888d75b86bb37970b5698e15" -dependencies = [ - "pin-project-internal 0.4.27", -] - [[package]] name = "pin-project" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a83804639aad6ba65345661744708855f9fbcb71176ea8d28d05aeb11d975e7" dependencies = [ - "pin-project-internal 1.0.3", -] - -[[package]] -name = "pin-project-internal" -version = "0.4.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "pin-project-internal", ] [[package]] @@ -1031,15 +750,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" - -[[package]] -name = "pin-project-lite" -version = "0.2.1" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36743d754ccdf9954c2e352ce2d4b106e024c814f6499c2dadff80da9a442d8" +checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" [[package]] name = "pin-utils" @@ -1059,30 +772,6 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro-hack" version = "0.5.19" @@ -1104,12 +793,6 @@ dependencies = [ "unicode-xid", ] -[[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.8" @@ -1125,11 +808,23 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom", + "getrandom 0.1.16", "libc", - "rand_chacha", - "rand_core", - "rand_hc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc 0.2.0", +] + +[[package]] +name = "rand" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +dependencies = [ + "libc", + "rand_chacha 0.3.0", + "rand_core 0.6.2", + "rand_hc 0.3.0", ] [[package]] @@ -1139,7 +834,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.2", ] [[package]] @@ -1148,7 +853,16 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom", + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" +dependencies = [ + "getrandom 0.2.2", ] [[package]] @@ -1157,7 +871,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ - "rand_core", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_hc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +dependencies = [ + "rand_core 0.6.2", ] [[package]] @@ -1168,21 +891,20 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "regex" -version = "1.4.2" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" +checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19" dependencies = [ "aho-corasick", "memchr", "regex-syntax", - "thread_local", ] [[package]] name = "regex-syntax" -version = "0.6.21" +version = "0.6.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" +checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548" [[package]] name = "remove_dir_all" @@ -1190,7 +912,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" dependencies = [ - "winapi 0.3.9", + "winapi", ] [[package]] @@ -1206,7 +928,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" dependencies = [ "lazy_static", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -1254,9 +976,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.61" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a" +checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" dependencies = [ "itoa", "ryu", @@ -1265,37 +987,39 @@ dependencies = [ [[package]] name = "serde_urlencoded" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" +checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" dependencies = [ - "dtoa", + "form_urlencoded", "itoa", + "ryu", "serde", - "url", ] [[package]] name = "sha-1" -version = "0.8.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +checksum = "dfebf75d25bd900fd1e7d11501efab59bc846dbc76196839663e6637bba9f25f" dependencies = [ "block-buffer", + "cfg-if 1.0.0", + "cpuid-bool", "digest", - "fake-simd", "opaque-debug", ] [[package]] name = "sha2" -version = "0.8.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" +checksum = "fa827a14b29ab7f44778d14a88d3cb76e949c45083f7dbfa507d0cb699dc12de" dependencies = [ "block-buffer", + "cfg-if 1.0.0", + "cpuid-bool", "digest", - "fake-simd", "opaque-debug", ] @@ -1322,7 +1046,7 @@ checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" dependencies = [ "cfg-if 1.0.0", "libc", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -1332,14 +1056,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2" dependencies = [ "libc", - "winapi 0.3.9", + "winapi", ] [[package]] name = "subtle" -version = "1.0.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" +checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2" [[package]] name = "syn" @@ -1360,10 +1084,10 @@ checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" dependencies = [ "cfg-if 0.1.10", "libc", - "rand", + "rand 0.7.3", "redox_syscall", "remove_dir_all", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -1395,15 +1119,6 @@ dependencies = [ "syn", ] -[[package]] -name = "thread_local" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb9bc092d0d51e76b2b19d9d85534ffc9ec2db959a2523cdae0697e2972cd447" -dependencies = [ - "lazy_static", -] - [[package]] name = "time" version = "0.1.44" @@ -1412,7 +1127,7 @@ checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" dependencies = [ "libc", "wasi 0.10.0+wasi-snapshot-preview1", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -1430,29 +1145,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" -[[package]] -name = "tokio" -version = "0.2.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099837d3464c16a808060bb3f02263b412f6fafcb5d01c533d309985fbeebe48" -dependencies = [ - "bytes 0.5.6", - "fnv", - "futures-core", - "iovec", - "lazy_static", - "libc", - "memchr", - "mio 0.6.23", - "mio-named-pipes", - "mio-uds", - "pin-project-lite 0.1.11", - "signal-hook-registry", - "slab", - "tokio-macros", - "winapi 0.3.9", -] - [[package]] name = "tokio" version = "1.5.0" @@ -1460,27 +1152,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83f0c8e7c0addab50b663055baf787d0af7f413a46e6e7fb9559a4e4db7137a5" dependencies = [ "autocfg", + "bytes 1.0.1", "libc", - "mio 0.7.11", + "mio", "num_cpus", - "pin-project-lite 0.2.1", + "once_cell", + "pin-project-lite", + "signal-hook-registry", + "tokio-macros", + "winapi", ] [[package]] name = "tokio-io-timeout" -version = "0.4.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9390a43272c8a6ac912ed1d1e2b6abeafd5047e05530a2fa304deee041a06215" +checksum = "90c49f106be240de154571dd31fbe48acb10ba6c6dd6f6517ad603abffa42de9" dependencies = [ - "bytes 0.5.6", - "tokio 0.2.24", + "pin-project-lite", + "tokio", ] [[package]] name = "tokio-macros" -version = "0.2.6" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a" +checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57" dependencies = [ "proc-macro2", "quote", @@ -1488,41 +1185,27 @@ dependencies = [ ] [[package]] -name = "tokio-tls" -version = "0.3.1" +name = "tokio-native-tls" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a70f4fcd7b3b24fb194f837560168208f669ca8cb70d0c4b862944452396343" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" dependencies = [ "native-tls", - "tokio 0.2.24", -] - -[[package]] -name = "tokio-util" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "571da51182ec208780505a32528fc5512a8fe1443ab960b3f2f3ef093cd16930" -dependencies = [ - "bytes 0.5.6", - "futures-core", - "futures-sink", - "log", - "pin-project-lite 0.1.11", - "tokio 0.2.24", + "tokio", ] [[package]] name = "tokio-util" -version = "0.3.1" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" +checksum = "940a12c99365c31ea8dd9ba04ec1be183ffe4920102bb7122c2f515437601e8e" dependencies = [ - "bytes 0.5.6", + "bytes 1.0.1", "futures-core", "futures-sink", "log", - "pin-project-lite 0.1.11", - "tokio 0.2.24", + "pin-project-lite", + "tokio", ] [[package]] @@ -1538,8 +1221,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3" dependencies = [ "cfg-if 1.0.0", - "log", - "pin-project-lite 0.2.1", + "pin-project-lite", "tracing-core", ] @@ -1552,16 +1234,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "tracing-futures" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c" -dependencies = [ - "pin-project 0.4.27", - "tracing", -] - [[package]] name = "try-lock" version = "0.2.3" @@ -1570,20 +1242,21 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" [[package]] name = "tungstenite" -version = "0.10.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfea31758bf674f990918962e8e5f07071a3161bd7c4138ed23e416e1ac4264e" +checksum = "5fe8dada8c1a3aeca77d6b51a4f1314e0f4b8e438b7b1b71e3ddaca8080e4093" dependencies = [ - "base64 0.11.0", + "base64", "byteorder", - "bytes 0.5.6", + "bytes 1.0.1", "http", "httparse", "input_buffer", "log", "native-tls", - "rand", + "rand 0.8.3", "sha-1", + "thiserror", "url", "utf-8", ] @@ -1620,14 +1293,14 @@ checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "url" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" +checksum = "9ccd964113622c8e9322cfac19eb1004a07e636c545f325da085d5cdde6f1f8b" dependencies = [ "form_urlencoded", "idna", "matches", - "percent-encoding 2.1.0", + "percent-encoding", ] [[package]] @@ -1670,12 +1343,6 @@ version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" - [[package]] name = "winapi" version = "0.3.9" @@ -1686,12 +1353,6 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" - [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -1704,7 +1365,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "winapi 0.3.9", + "winapi", ] [[package]] @@ -1712,13 +1373,3 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "ws2_32-sys" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] diff --git a/edge-modules/api-proxy-module/Cargo.toml b/edge-modules/api-proxy-module/Cargo.toml index 58e0b2700bc..90b6b6c4f84 100644 --- a/edge-modules/api-proxy-module/Cargo.toml +++ b/edge-modules/api-proxy-module/Cargo.toml @@ -7,26 +7,25 @@ edition = "2018" [dependencies] anyhow = "1.0" -base64 = "0.11" +base64 = "0.13" chrono = "0.4" -env_logger = "0.7" +env_logger = "0.8" envsubst = "0.2" -futures = "0.3" futures-util = "0.3" log = "0.4" md5 = "0.7" -regex = "1.3" +regex = "1.4" serde_json = "1.0" -tokio = { version = "0.2", features = ["tcp", "time", "process","signal","sync"] } -percent-encoding = "1.0" -url = "2.1" +tokio = { version = "1.5", features = ["signal", "rt-multi-thread"] } +percent-encoding = "2.1" +url = "2.2" azure-iot-mqtt = { path = "./rust-sdk/azure-iot-mqtt" } -edgelet-client = { git = "https://github.com/Azure/iotedge", rev = "4cd508" } -mqtt3 = { git = "https://github.com/Azure/iotedge", rev = "4cd508" } +edgelet-client = { path = "../../mqtt/edgelet-client" } +mqtt3 = { path = "../../mqtt/mqtt3" } [dev-dependencies] lazy_static = "1.4" matches = "0.1" -mockito = "0.25" -tokio = { version = "0.2", features = ["macros"] } +mockito = "0.30" +tokio = { version = "1.5", features = ["macros"] } diff --git a/edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/Cargo.lock b/edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/Cargo.lock new file mode 100644 index 00000000000..d380778010a --- /dev/null +++ b/edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/Cargo.lock @@ -0,0 +1,1222 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "azure-iot-mqtt" +version = "0.1.0" +dependencies = [ + "base64", + "bytes", + "env_logger", + "futures-channel", + "futures-core", + "futures-util", + "hex", + "hmac", + "http", + "hyper", + "hyperlocal", + "lazy_static", + "log", + "mqtt3", + "native-tls", + "percent-encoding", + "regex", + "serde", + "serde_derive", + "serde_json", + "sha2", + "structopt", + "tokio", + "tokio-io-timeout", + "tokio-native-tls", + "tungstenite", + "url", +] + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" + +[[package]] +name = "cc" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "core-foundation" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" + +[[package]] +name = "cpuid-bool" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" + +[[package]] +name = "crypto-mac" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4857fd85a0c34b3c3297875b747c1e02e06b6a0ea32dd892d8192b9ce0813ea6" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "env_logger" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17392a012ea30ef05a610aa97dfb49496e71c9f676b27879922ea5bdf60d9d3f" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[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 = "futures-channel" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce79c6a52a299137a6013061e0cf0e688fce5d7f1bc60125f520912fdb29ec25" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "098cd1c6dda6ca01650f1a37a794245eb73181d0d4d4e955e2f3c37db7af1815" + +[[package]] +name = "futures-macro" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668c6733a182cd7deb4f1de7ba3bf2120823835b3bcfbeacf7d2c4a773c1bb8b" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5629433c555de3d82861a7a4e3794a4c40040390907cfbfd7143a92a426c23" + +[[package]] +name = "futures-task" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba7aa51095076f3ba6d9a1f702f74bd05ec65f555d70d2033d55ba8d69f581bc" + +[[package]] +name = "futures-util" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c144ad54d60f23927f0a6b6d816e4271278b64f005ad65e4e35291d2de9c025" +dependencies = [ + "futures-core", + "futures-macro", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "heck" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +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.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" +dependencies = [ + "crypto-mac", + "digest", +] + +[[package]] +name = "http" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfb77c123b4e2f72a2069aeae0b4b4949cc7e966df277813fc16347e7549737" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a1ce40d6fc9764887c2fdc7305c3dcc429ba11ff981c1509416afd5697e4437" + +[[package]] +name = "httpdate" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf09f61b52cfcf4c00de50df88ae423d6c02354e385a86341133b5338630ad1" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyperlocal" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fafdf7b2b2de7c9784f76e02c0935e65a8117ec3b768644379983ab333ac98c" +dependencies = [ + "futures-util", + "hex", + "hyper", + "pin-project", + "tokio", +] + +[[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 = "input_buffer" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f97967975f448f1a7ddb12b0bc41069d09ed6a1c161a92687e057325db35d413" +dependencies = [ + "bytes", +] + +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + +[[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.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + +[[package]] +name = "memchr" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" + +[[package]] +name = "mio" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "mqtt3" +version = "0.1.0" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-sink", + "futures-util", + "log", + "tokio", + "tokio-util", +] + +[[package]] +name = "native-tls" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8d96b2e1c8da3957d58100b09f102c6d9cfdfced01b7ec5a8974044bb09dbd4" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl" +version = "0.10.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a61075b62a23fef5a29815de7536d940aa35ce96d18ce0cc5076272db678a577" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-sys", +] + +[[package]] +name = "openssl-probe" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" + +[[package]] +name = "openssl-sys" +version = "0.9.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "313752393519e876837e09e1fa183ddef0be7735868dced3196f4472d536277f" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[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.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7509cc106041c40a4518d2af7a61530e1eed0e6285296a3d8c5472806ccc4a4" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c950132583b500556b1efd71d45b319029f2b71518d979fcc208e16b42426f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" + +[[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.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro-nested" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" + +[[package]] +name = "proc-macro2" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8270314b5ccceb518e7e578952f0b72b88222d02e8f77f5ecf7abbb673539041" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "schannel" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +dependencies = [ + "lazy_static", + "winapi", +] + +[[package]] +name = "security-framework" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3670b1d2fdf6084d192bc71ead7aabe6c06aa2ea3fbd9cc3ac111fa5c2b1bd84" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3676258fd3cfe2c9a0ec99ce3038798d847ce3e4bb17746373eb9f0f1ac16339" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha-1" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfebf75d25bd900fd1e7d11501efab59bc846dbc76196839663e6637bba9f25f" +dependencies = [ + "block-buffer", + "cfg-if", + "cpuid-bool", + "digest", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa827a14b29ab7f44778d14a88d3cb76e949c45083f7dbfa507d0cb699dc12de" +dependencies = [ + "block-buffer", + "cfg-if", + "cpuid-bool", + "digest", + "opaque-debug", +] + +[[package]] +name = "signal-hook-registry" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "socket2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "subtle" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2" + +[[package]] +name = "syn" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +dependencies = [ + "cfg-if", + "libc", + "rand", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342" +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.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83f0c8e7c0addab50b663055baf787d0af7f413a46e6e7fb9559a4e4db7137a5" +dependencies = [ + "autocfg", + "bytes", + "libc", + "mio", + "num_cpus", + "once_cell", + "pin-project-lite", + "signal-hook-registry", + "winapi", +] + +[[package]] +name = "tokio-io-timeout" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90c49f106be240de154571dd31fbe48acb10ba6c6dd6f6517ad603abffa42de9" +dependencies = [ + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940a12c99365c31ea8dd9ba04ec1be183ffe4920102bb7122c2f515437601e8e" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + +[[package]] +name = "tracing" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "tungstenite" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fe8dada8c1a3aeca77d6b51a4f1314e0f4b8e438b7b1b71e3ddaca8080e4093" +dependencies = [ + "base64", + "byteorder", + "bytes", + "http", + "httparse", + "input_buffer", + "log", + "native-tls", + "rand", + "sha-1", + "thiserror", + "url", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" + +[[package]] +name = "unicode-bidi" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" + +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "url" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ccd964113622c8e9322cfac19eb1004a07e636c545f325da085d5cdde6f1f8b" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "vcpkg" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbdbff6266a24120518560b5dc983096efb98462e51d0d68169895b237be3e5d" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[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.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/Cargo.toml b/edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/Cargo.toml index 02bfd268a1e..ecefca2db48 100644 --- a/edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/Cargo.toml +++ b/edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/Cargo.toml @@ -6,33 +6,33 @@ license = "MIT" edition = "2018" [dependencies] -base64 = "0.11" -bytes = "0.5" +base64 = "0.13" +bytes = "1.0" futures-core = "0.3" futures-channel = "0.3" futures-util = "0.3" -hmac = "0.7" +hmac = "0.10" http = "0.2" -hyper = { version = "0.13", features = ["stream"] } -hyper-uds = { path = "../hyper-uds" } -lazy_static = "1.2" +hyper = { version = "0.14", features = ["client", "http1", "tcp", "stream"] } +hyperlocal = "0.8" +lazy_static = "1.4" log = "0.4" native-tls = "0.2" -percent-encoding = "2" -regex = "1.1" -serde = { version = "1", features = ["derive", "rc"] } -serde_derive = "1" -serde_json = "1" -sha2 = "0.8" -tokio = { version = "0.2", features = ["tcp", "time", "process"] } -tokio-io-timeout = "0.4" -tokio-tls = "0.3" -tungstenite = "0.10" -url = "2" +percent-encoding = "2.1" +regex = "1.4" +serde = { version = "1.0", features = ["derive", "rc"] } +serde_derive = "1.0" +serde_json = "1.0" +sha2 = "0.9" +tokio = { version = "1.5", features = ["time", "process"] } +tokio-io-timeout = "1.1" +tokio-native-tls = "0.3" +tungstenite = "0.13" +url = "2.2" -mqtt3 = { git = "https://github.com/Azure/iotedge", rev = "4cd508" } +mqtt3 = { path = "../../../../mqtt/mqtt3" } [dev-dependencies] -env_logger = "0.7" +env_logger = "0.8" structopt = "0.3" -tokio = { version = "0.2", features = ["signal", "stream"] } +tokio = { version = "1.5", features = ["signal"] } diff --git a/edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/README.md b/edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/README.md index 571beb5d04d..76755537182 100644 --- a/edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/README.md +++ b/edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/README.md @@ -14,7 +14,7 @@ An Azure IoT client library. It implements [the Azure IoT Hub MQTT protocol.](ht - Transparently reconnects when connection is broken or protocol errors, with back-off. -- Standard futures 0.1 and tokio 0.1 interface. The client is just a `futures::Stream` of events received from the server. +- Standard futures and tokio1 interface. The client is just a `futures::Stream` of events received from the server. - Supports being used by an edge module to talk to an IoT Edge Hub. diff --git a/edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/src/io.rs b/edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/src/io.rs index 7679f901514..c793bbf926b 100644 --- a/edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/src/io.rs +++ b/edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/src/io.rs @@ -4,392 +4,517 @@ use std::future::Future; /// A [`mqtt3::IoSource`] implementation used by the clients. pub struct IoSource { - iothub_hostname: std::sync::Arc, - iothub_host: std::net::SocketAddr, - authentication: crate::Authentication, - timeout: std::time::Duration, - extra: IoSourceExtra, + iothub_hostname: std::sync::Arc, + iothub_host: std::net::SocketAddr, + authentication: crate::Authentication, + timeout: std::time::Duration, + extra: IoSourceExtra, } #[derive(Clone, Debug)] enum IoSourceExtra { - Raw, - - WebSocket { - uri: http::Uri, - }, + Raw, + WebSocket { uri: http::Uri }, } impl IoSource { - pub(crate) fn new( - iothub_hostname: std::sync::Arc, - authentication: crate::Authentication, - timeout: std::time::Duration, - transport: crate::Transport, - ) -> Result { - let port = match transport { - crate::Transport::Tcp => 8883, - crate::Transport::WebSocket => 443, - }; - - let iothub_host = - std::net::ToSocketAddrs::to_socket_addrs(&(&*iothub_hostname, port)) - .map_err(|err| crate::CreateClientError::ResolveIotHubHostname(Some(err)))? - .next() - .ok_or(crate::CreateClientError::ResolveIotHubHostname(None))?; - - let extra = match transport { - crate::Transport::Tcp => crate::io::IoSourceExtra::Raw, - - crate::Transport::WebSocket => { - let uri = match format!("ws://{}/$iothub/websocket", iothub_hostname).parse() { - Ok(uri) => uri, - Err(err) => return Err(crate::CreateClientError::WebSocketUrl(err)), - }; - - crate::io::IoSourceExtra::WebSocket { - uri, - } - }, - }; - - Ok(IoSource { - iothub_hostname, - iothub_host, - authentication, - timeout, - extra, - }) - } + pub(crate) fn new( + iothub_hostname: std::sync::Arc, + authentication: crate::Authentication, + timeout: std::time::Duration, + transport: crate::Transport, + ) -> Result { + let port = match transport { + crate::Transport::Tcp => 8883, + crate::Transport::WebSocket => 443, + }; + + let iothub_host = std::net::ToSocketAddrs::to_socket_addrs(&(&*iothub_hostname, port)) + .map_err(|err| crate::CreateClientError::ResolveIotHubHostname(Some(err)))? + .next() + .ok_or(crate::CreateClientError::ResolveIotHubHostname(None))?; + + let extra = match transport { + crate::Transport::Tcp => crate::io::IoSourceExtra::Raw, + + crate::Transport::WebSocket => { + let uri = match format!("ws://{}/$iothub/websocket", iothub_hostname).parse() { + Ok(uri) => uri, + Err(err) => return Err(crate::CreateClientError::WebSocketUrl(err)), + }; + + crate::io::IoSourceExtra::WebSocket { uri } + } + }; + + Ok(IoSource { + iothub_hostname, + iothub_host, + authentication, + timeout, + extra, + }) + } } -pub const IOTHUB_ENCODE_SET: &percent_encoding::AsciiSet = - &percent_encoding::CONTROLS // C0 control - .add(b' ').add(b'"').add(b'<').add(b'>').add(b'`') // fragment - .add(b'#').add(b'?').add(b'{').add(b'}') // path - .add(b'='); +pub const FRAGMENT_ENCODE_SET: &percent_encoding::AsciiSet = &percent_encoding::CONTROLS + .add(b' ') + .add(b'"') + .add(b'<') + .add(b'>') + .add(b'`'); + +pub const PATH_ENCODE_SET: &percent_encoding::AsciiSet = + &FRAGMENT_ENCODE_SET.add(b'#').add(b'?').add(b'{').add(b'}'); + +pub const IOTHUB_ENCODE_SET: &percent_encoding::AsciiSet = &PATH_ENCODE_SET.add(b'='); impl mqtt3::IoSource for IoSource { - type Io = Io>>; - type Error = std::io::Error; - #[allow(clippy::type_complexity)] - type Future = std::pin::Pin)>> + Send>>; - - fn connect(&mut self) -> Self::Future { - use futures_util::FutureExt; - - #[allow(clippy::identity_op)] - const DEFAULT_MAX_TOKEN_VALID_DURATION: std::time::Duration = std::time::Duration::from_secs(1 * 60 * 60); - - let iothub_hostname = self.iothub_hostname.clone(); - let timeout = self.timeout; - let extra = self.extra.clone(); - - let authentication = match &self.authentication { - crate::Authentication::SasKey { device_id, key, max_token_valid_duration, server_root_certificate } => - match prepare_sas_token_request(&*iothub_hostname, device_id, None, *max_token_valid_duration) { - Ok((signature_data, make_sas_token)) => { - use hmac::Mac; - - let mut mac = hmac::Hmac::::new_varkey(key).expect("HMAC can have invalid key length"); - mac.input(signature_data.as_bytes()); - let signature = mac.result().code(); - let signature = base64::encode(signature.as_slice()); - - let sas_token = make_sas_token(&signature); - - futures_util::future::Either::Left(futures_util::future::ok((Some(sas_token), None, server_root_certificate.clone()))) - }, - - Err(err) => futures_util::future::Either::Left(futures_util::future::err(err)), - }, - - crate::Authentication::SasToken { token, server_root_certificate } => - futures_util::future::Either::Left(futures_util::future::ok((Some(token.to_owned()), None, server_root_certificate.clone()))), - - crate::Authentication::Certificate { der, password, server_root_certificate } => - match native_tls::Identity::from_pkcs12(der, password) { - Ok(identity) => futures_util::future::Either::Left(futures_util::future::ok((None, Some(identity), server_root_certificate.clone()))), - Err(err) => futures_util::future::Either::Left(futures_util::future::err( - std::io::Error::new(std::io::ErrorKind::Other, format!("could not parse client certificate: {}", err)))), - }, - - crate::Authentication::IotEdge { device_id, module_id, generation_id, iothub_hostname, workload_url } => - match crate::iotedge_client::Client::new(workload_url) { - Ok(iotedge_client) => { - match prepare_sas_token_request(iothub_hostname, device_id, None, DEFAULT_MAX_TOKEN_VALID_DURATION) { - Ok((signature_data, make_sas_token)) => { - let signature = iotedge_client.hmac_sha256(module_id, generation_id, &signature_data); - - let server_root_certificate = iotedge_client.get_server_root_certificate(); - - futures_util::future::Either::Right(futures_util::future::try_join(signature, server_root_certificate) - .map(move |result| match result { - Ok((signature, server_root_certificate)) => { - let sas_token = make_sas_token(&signature); - Ok((Some(sas_token), None, server_root_certificate)) - }, - - Err(err) => Err(std::io::Error::new(std::io::ErrorKind::Other, err)), - })) - }, - - Err(err) => futures_util::future::Either::Left(futures_util::future::err(err)), - } - }, - - Err(err) => futures_util::future::Either::Left(futures_util::future::err( - std::io::Error::new(std::io::ErrorKind::Other, format!("could not initialize iotedge client: {}", err)))), - }, - }; - - let iothub_host = self.iothub_host.to_owned(); - - Box::pin(async move { - let stream = async { - let stream = - tokio::time::timeout(timeout, tokio::net::TcpStream::connect(&iothub_host)).await - .map_err(|_| std::io::ErrorKind::TimedOut)?; - Ok(stream) - }; - - let ((password, identity, server_root_certificate), stream) = futures_util::future::try_join(authentication, stream).await?; - - let stream = stream?; - stream.set_nodelay(true)?; - - let mut stream = tokio_io_timeout::TimeoutStream::new(stream); - stream.set_read_timeout(Some(timeout)); - - let mut tls_connector_builder = native_tls::TlsConnector::builder(); - if let Some(identity) = identity { - tls_connector_builder.identity(identity); - } - for certificate in server_root_certificate { - tls_connector_builder.add_root_certificate(certificate); - } - - let connector = - tls_connector_builder.build() - .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, format!("could not create TLS connector: {}", err)))?; - let connector: tokio_tls::TlsConnector = connector.into(); - - let stream = - connector.connect(&iothub_hostname, stream).await - .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?; - - match extra { - IoSourceExtra::Raw => Ok((Io::Raw(stream), password)), - - IoSourceExtra::WebSocket { uri } => { - let request = - http::Request::get(uri) - .header("sec-websocket-protocol", "mqtt") - .body(()) - .expect("building client handshake request cannot fail"); - - let handshake = - tungstenite::ClientHandshake::start(TokioToStd { tokio: stream, cx: std::ptr::null_mut() }, request, None) - .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?; - let stream = WsConnect::Handshake(handshake).await?; - Ok(( - Io::WebSocket { - inner: stream, - pending_read: std::io::Cursor::new(vec![]), - }, - password, - )) - }, - } - }) - } + type Io = Io< + tokio_native_tls::TlsStream< + std::pin::Pin>>, + >, + >; + type Error = std::io::Error; + #[allow(clippy::type_complexity)] + type Future = + std::pin::Pin)>> + Send>>; + + fn connect(&mut self) -> Self::Future { + use futures_util::FutureExt; + + #[allow(clippy::identity_op)] + const DEFAULT_MAX_TOKEN_VALID_DURATION: std::time::Duration = + std::time::Duration::from_secs(1 * 60 * 60); + + let iothub_hostname = self.iothub_hostname.clone(); + let timeout = self.timeout; + let extra = self.extra.clone(); + + let authentication = match &self.authentication { + crate::Authentication::SasKey { + device_id, + key, + max_token_valid_duration, + server_root_certificate, + } => match prepare_sas_token_request( + &*iothub_hostname, + device_id, + None, + *max_token_valid_duration, + ) { + Ok((signature_data, make_sas_token)) => { + use hmac::{Mac, NewMac}; + + let mut mac = hmac::Hmac::::new_varkey(key) + .expect("HMAC can have invalid key length"); + mac.update(signature_data.as_bytes()); + let signature = mac.finalize(); + let signature = base64::encode(signature.into_bytes()); + + let sas_token = make_sas_token(&signature); + + futures_util::future::Either::Left(futures_util::future::ok(( + Some(sas_token), + None, + server_root_certificate.clone(), + ))) + } + + Err(err) => futures_util::future::Either::Left(futures_util::future::err(err)), + }, + + crate::Authentication::SasToken { + token, + server_root_certificate, + } => futures_util::future::Either::Left(futures_util::future::ok(( + Some(token.to_owned()), + None, + server_root_certificate.clone(), + ))), + + crate::Authentication::Certificate { + der, + password, + server_root_certificate, + } => match native_tls::Identity::from_pkcs12(der, password) { + Ok(identity) => futures_util::future::Either::Left(futures_util::future::ok(( + None, + Some(identity), + server_root_certificate.clone(), + ))), + Err(err) => futures_util::future::Either::Left(futures_util::future::err( + std::io::Error::new( + std::io::ErrorKind::Other, + format!("could not parse client certificate: {}", err), + ), + )), + }, + + crate::Authentication::IotEdge { + device_id, + module_id, + generation_id, + iothub_hostname, + workload_url, + } => match crate::iotedge_client::Client::new(workload_url) { + Ok(iotedge_client) => { + match prepare_sas_token_request( + iothub_hostname, + device_id, + None, + DEFAULT_MAX_TOKEN_VALID_DURATION, + ) { + Ok((signature_data, make_sas_token)) => { + let signature = iotedge_client.hmac_sha256( + module_id, + generation_id, + &signature_data, + ); + + let server_root_certificate = + iotedge_client.get_server_root_certificate(); + + futures_util::future::Either::Right( + futures_util::future::try_join(signature, server_root_certificate) + .map(move |result| match result { + Ok((signature, server_root_certificate)) => { + let sas_token = make_sas_token(&signature); + Ok((Some(sas_token), None, server_root_certificate)) + } + + Err(err) => { + Err(std::io::Error::new(std::io::ErrorKind::Other, err)) + } + }), + ) + } + + Err(err) => { + futures_util::future::Either::Left(futures_util::future::err(err)) + } + } + } + + Err(err) => futures_util::future::Either::Left(futures_util::future::err( + std::io::Error::new( + std::io::ErrorKind::Other, + format!("could not initialize iotedge client: {}", err), + ), + )), + }, + }; + + let iothub_host = self.iothub_host.to_owned(); + + Box::pin(async move { + let stream = async { + let stream = + tokio::time::timeout(timeout, tokio::net::TcpStream::connect(&iothub_host)) + .await + .map_err(|_| std::io::ErrorKind::TimedOut)?; + Ok(stream) + }; + + let ((password, identity, server_root_certificate), stream) = + futures_util::future::try_join(authentication, stream).await?; + + let stream = stream?; + stream.set_nodelay(true)?; + + let mut stream = tokio_io_timeout::TimeoutStream::new(stream); + stream.set_read_timeout(Some(timeout)); + + let mut tls_connector_builder = native_tls::TlsConnector::builder(); + if let Some(identity) = identity { + tls_connector_builder.identity(identity); + } + for certificate in server_root_certificate { + tls_connector_builder.add_root_certificate(certificate); + } + + let connector = tls_connector_builder.build().map_err(|err| { + std::io::Error::new( + std::io::ErrorKind::Other, + format!("could not create TLS connector: {}", err), + ) + })?; + let connector: tokio_native_tls::TlsConnector = connector.into(); + + let stream = connector + .connect(&iothub_hostname, Box::pin(stream)) + .await + .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?; + + match extra { + IoSourceExtra::Raw => Ok((Io::Raw(stream), password)), + + IoSourceExtra::WebSocket { uri } => { + let request = http::Request::get(uri) + .header("sec-websocket-protocol", "mqtt") + .body(()) + .expect("building client handshake request cannot fail"); + + let handshake = tungstenite::ClientHandshake::start( + TokioToStd { + tokio: stream, + cx: std::ptr::null_mut(), + }, + request, + None, + ) + .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?; + let stream = WsConnect::Handshake(handshake).await?; + Ok(( + Io::WebSocket { + inner: stream, + pending_read: std::io::Cursor::new(vec![]), + }, + password, + )) + } + } + }) + } } /// The transport to use for the connection to the Azure IoT Hub #[derive(Clone, Copy, Debug)] pub enum Transport { - Tcp, - WebSocket, + Tcp, + WebSocket, } -enum WsConnect where S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin { - Handshake(tungstenite::handshake::MidHandshake>>), - Invalid, +enum WsConnect +where + S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin, +{ + Handshake(tungstenite::handshake::MidHandshake>>), + Invalid, } -impl std::fmt::Debug for WsConnect where S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - WsConnect::Handshake(_) => f.debug_struct("Handshake").finish(), - WsConnect::Invalid => f.debug_struct("Invalid").finish(), - } - } +impl std::fmt::Debug for WsConnect +where + S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + WsConnect::Handshake(_) => f.debug_struct("Handshake").finish(), + WsConnect::Invalid => f.debug_struct("Invalid").finish(), + } + } } -impl Future for WsConnect where S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin { - type Output = std::io::Result>>; - - fn poll(mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll { - match std::mem::replace(&mut *self, WsConnect::Invalid) { - WsConnect::Handshake(mut handshake) => { - handshake.get_mut().get_mut().set_cx(cx); - match handshake.handshake() { - Ok((mut stream, _)) => { - stream.get_mut().set_cx(std::ptr::null_mut()); - std::task::Poll::Ready(Ok(stream)) - }, - - Err(tungstenite::HandshakeError::Interrupted(mut handshake)) => { - handshake.get_mut().get_mut().set_cx(std::ptr::null_mut()); - *self = WsConnect::Handshake(handshake); - std::task::Poll::Pending - }, - - Err(tungstenite::HandshakeError::Failure(err)) => - poll_from_tungstenite_error(err), - } - }, - - WsConnect::Invalid => - panic!("future polled after completion"), - } - } +impl Future for WsConnect +where + S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin, +{ + type Output = std::io::Result>>; + + fn poll( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + match std::mem::replace(&mut *self, WsConnect::Invalid) { + WsConnect::Handshake(mut handshake) => { + handshake.get_mut().get_mut().set_cx(cx); + match handshake.handshake() { + Ok((mut stream, _)) => { + stream.get_mut().set_cx(std::ptr::null_mut()); + std::task::Poll::Ready(Ok(stream)) + } + + Err(tungstenite::HandshakeError::Interrupted(mut handshake)) => { + handshake.get_mut().get_mut().set_cx(std::ptr::null_mut()); + *self = WsConnect::Handshake(handshake); + std::task::Poll::Pending + } + + Err(tungstenite::HandshakeError::Failure(err)) => { + poll_from_tungstenite_error(err) + } + } + } + + WsConnect::Invalid => panic!("future polled after completion"), + } + } } /// A wrapper around an inner I/O object pub enum Io { - Raw(S), + Raw(S), - WebSocket { - inner: tungstenite::WebSocket>, - pending_read: std::io::Cursor>, - }, + WebSocket { + inner: tungstenite::WebSocket>, + pending_read: std::io::Cursor>, + }, } -impl tokio::io::AsyncRead for Io where S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin { - fn poll_read(mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, buf: &mut [u8]) -> std::task::Poll> { - use std::io::Read; - - let (inner, pending_read) = match &mut *self { - Io::Raw(stream) => return std::pin::Pin::new(stream).poll_read(cx, buf), - Io::WebSocket { inner, pending_read } => (inner, pending_read), - }; - - if buf.is_empty() { - return std::task::Poll::Ready(Ok(0)); - } - - loop { - if pending_read.position() != pending_read.get_ref().len() as u64 { - return std::task::Poll::Ready(Ok(pending_read.read(buf).expect("Cursor::read cannot fail"))); - } - - inner.get_mut().set_cx(cx); - let message = inner.read_message(); - inner.get_mut().set_cx(std::ptr::null_mut()); - - match message { - Ok(tungstenite::Message::Binary(b)) => *pending_read = std::io::Cursor::new(b), - Ok(message) => log::warn!("ignoring unexpected message: {:?}", message), - Err(err) => return poll_from_tungstenite_error(err), - }; - } - } +impl tokio::io::AsyncRead for Io +where + S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin, +{ + fn poll_read( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut tokio::io::ReadBuf<'_>, + ) -> std::task::Poll> { + use std::io::Read; + + let (inner, pending_read) = match &mut *self { + Io::Raw(stream) => return std::pin::Pin::new(stream).poll_read(cx, buf), + Io::WebSocket { + inner, + pending_read, + } => (inner, pending_read), + }; + + if buf.remaining() == 0 { + return std::task::Poll::Ready(Ok(())); + } + + loop { + if pending_read.position() != pending_read.get_ref().len() as u64 { + let read = pending_read + .read(buf.initialize_unfilled()) + .expect("Cursor::read cannot fail"); + buf.advance(read); + + return std::task::Poll::Ready(Ok(())); + } + + inner.get_mut().set_cx(cx); + let message = inner.read_message(); + inner.get_mut().set_cx(std::ptr::null_mut()); + + match message { + Ok(tungstenite::Message::Binary(b)) => *pending_read = std::io::Cursor::new(b), + Ok(message) => log::warn!("ignoring unexpected message: {:?}", message), + Err(err) => return poll_from_tungstenite_error(err), + }; + } + } } -impl tokio::io::AsyncWrite for Io where S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin { - fn poll_write(mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, buf: &[u8]) -> std::task::Poll> { - let inner = match &mut *self { - Io::Raw(stream) => return std::pin::Pin::new(stream).poll_write(cx, buf), - Io::WebSocket { inner, .. } => inner, - }; - - if buf.is_empty() { - return std::task::Poll::Ready(Ok(0)); - } - - let message = tungstenite::Message::Binary(buf.to_owned()); - - inner.get_mut().set_cx(cx); - let result = inner.write_message(message); - inner.get_mut().set_cx(std::ptr::null_mut()); - - match result { - Ok(()) => std::task::Poll::Ready(Ok(buf.len())), - Err(tungstenite::Error::SendQueueFull(_)) => std::task::Poll::Pending, // Hope client calls `poll_flush()` before retrying - Err(err) => poll_from_tungstenite_error(err), - } - } - - fn poll_flush(mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll> { - let inner = match &mut *self { - Io::Raw(stream) => return std::pin::Pin::new(stream).poll_flush(cx), - Io::WebSocket { inner, .. } => inner, - }; - - inner.get_mut().set_cx(cx); - let result = inner.write_pending(); - inner.get_mut().set_cx(std::ptr::null_mut()); - match result { - Ok(()) => std::task::Poll::Ready(Ok(())), - Err(err) => poll_from_tungstenite_error(err), - } - } - - fn poll_shutdown(mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll> { - let inner = match &mut *self { - Io::Raw(stream) => return std::pin::Pin::new(stream).poll_shutdown(cx), - Io::WebSocket { inner, .. } => inner, - }; - - inner.get_mut().set_cx(cx); - let result = inner.close(None); - inner.get_mut().set_cx(std::ptr::null_mut()); - match result { - Ok(()) => std::task::Poll::Ready(Ok(())), - Err(err) => poll_from_tungstenite_error(err), - } - } +impl tokio::io::AsyncWrite for Io +where + S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin, +{ + fn poll_write( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + let inner = match &mut *self { + Io::Raw(stream) => return std::pin::Pin::new(stream).poll_write(cx, buf), + Io::WebSocket { inner, .. } => inner, + }; + + if buf.is_empty() { + return std::task::Poll::Ready(Ok(0)); + } + + let message = tungstenite::Message::Binary(buf.to_owned()); + + inner.get_mut().set_cx(cx); + let result = inner.write_message(message); + inner.get_mut().set_cx(std::ptr::null_mut()); + + match result { + Ok(()) => std::task::Poll::Ready(Ok(buf.len())), + Err(tungstenite::Error::SendQueueFull(_)) => std::task::Poll::Pending, // Hope client calls `poll_flush()` before retrying + Err(err) => poll_from_tungstenite_error(err), + } + } + + fn poll_flush( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + let inner = match &mut *self { + Io::Raw(stream) => return std::pin::Pin::new(stream).poll_flush(cx), + Io::WebSocket { inner, .. } => inner, + }; + + inner.get_mut().set_cx(cx); + let result = inner.write_pending(); + inner.get_mut().set_cx(std::ptr::null_mut()); + match result { + Ok(()) => std::task::Poll::Ready(Ok(())), + Err(err) => poll_from_tungstenite_error(err), + } + } + + fn poll_shutdown( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + let inner = match &mut *self { + Io::Raw(stream) => return std::pin::Pin::new(stream).poll_shutdown(cx), + Io::WebSocket { inner, .. } => inner, + }; + + inner.get_mut().set_cx(cx); + let result = inner.close(None); + inner.get_mut().set_cx(std::ptr::null_mut()); + match result { + Ok(()) => std::task::Poll::Ready(Ok(())), + Err(err) => poll_from_tungstenite_error(err), + } + } } fn poll_from_tungstenite_error(err: tungstenite::Error) -> std::task::Poll> { - match err { - tungstenite::Error::Io(ref err) if err.kind() == std::io::ErrorKind::WouldBlock => std::task::Poll::Pending, - tungstenite::Error::Io(err) => std::task::Poll::Ready(Err(err)), - err => std::task::Poll::Ready(Err(std::io::Error::new(std::io::ErrorKind::Other, err))), - } + match err { + tungstenite::Error::Io(ref err) if err.kind() == std::io::ErrorKind::WouldBlock => { + std::task::Poll::Pending + } + tungstenite::Error::Io(err) => std::task::Poll::Ready(Err(err)), + err => std::task::Poll::Ready(Err(std::io::Error::new(std::io::ErrorKind::Other, err))), + } } fn prepare_sas_token_request( - iothub_hostname: &str, - device_id: &str, - module_id: Option<&str>, - max_token_valid_duration: std::time::Duration, + iothub_hostname: &str, + device_id: &str, + module_id: Option<&str>, + max_token_valid_duration: std::time::Duration, ) -> std::io::Result<(String, impl FnOnce(&str) -> String)> { - let since_unix_epoch = - std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH) - .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, format!("could not get current time: {}", err)))?; - - let resource_uri = - if let Some(module_id) = module_id { - format!("{}/devices/{}/modules/{}", iothub_hostname, device_id, module_id) - } - else { - format!("{}/devices/{}", iothub_hostname, device_id) - }; - let resource_uri: String = percent_encoding::utf8_percent_encode(&resource_uri, IOTHUB_ENCODE_SET).collect(); - - let expiry = since_unix_epoch + max_token_valid_duration; - let expiry = expiry.as_secs().to_string(); - - let signature_data = format!("{}\n{}", resource_uri, expiry); - - Ok((signature_data, move |signature: &str| { - let mut serializer = url::form_urlencoded::Serializer::new(format!("SharedAccessSignature sr={}", resource_uri)); - serializer.append_pair("se", &expiry); - serializer.append_pair("sig", signature); - serializer.finish() - })) + let since_unix_epoch = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .map_err(|err| { + std::io::Error::new( + std::io::ErrorKind::Other, + format!("could not get current time: {}", err), + ) + })?; + + let resource_uri = if let Some(module_id) = module_id { + format!( + "{}/devices/{}/modules/{}", + iothub_hostname, device_id, module_id + ) + } else { + format!("{}/devices/{}", iothub_hostname, device_id) + }; + let resource_uri: String = + percent_encoding::utf8_percent_encode(&resource_uri, IOTHUB_ENCODE_SET).collect(); + + let expiry = since_unix_epoch + max_token_valid_duration; + let expiry = expiry.as_secs().to_string(); + + let signature_data = format!("{}\n{}", resource_uri, expiry); + + Ok((signature_data, move |signature: &str| { + let mut serializer = url::form_urlencoded::Serializer::new(format!( + "SharedAccessSignature sr={}", + resource_uri + )); + serializer.append_pair("se", &expiry); + serializer.append_pair("sig", signature); + serializer.finish() + })) } /// Implements `std::io::{Read, Write}` for a `tokio::io::Async{Read, Write}` @@ -399,52 +524,68 @@ fn prepare_sas_token_request( /// /// (Not part of public API, so it's not exported from the crate root.) pub struct TokioToStd { - tokio: T, - cx: *mut std::task::Context<'static>, + tokio: T, + cx: *mut std::task::Context<'static>, } impl TokioToStd { - fn set_cx(&mut self, cx: *mut std::task::Context<'_>) { - self.cx = cx as *mut std::ffi::c_void as *mut _; - } + fn set_cx(&mut self, cx: *mut std::task::Context<'_>) { + self.cx = cx.cast::().cast(); + } } -unsafe impl Send for TokioToStd where T: Send { } - -impl std::io::Read for TokioToStd where T: tokio::io::AsyncRead + Unpin { - fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - unsafe { - let cx = self.cx.as_mut().expect("TokioToStd used without setting task context"); - - match std::pin::Pin::new(&mut self.tokio).poll_read(cx, buf) { - std::task::Poll::Ready(Ok(read)) => Ok(read), - std::task::Poll::Ready(Err(err)) => Err(err), - std::task::Poll::Pending => Err(std::io::ErrorKind::WouldBlock.into()), - } - } - } +unsafe impl Send for TokioToStd where T: Send {} + +impl std::io::Read for TokioToStd +where + T: tokio::io::AsyncRead + Unpin, +{ + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + unsafe { + let cx = self + .cx + .as_mut() + .expect("TokioToStd used without setting task context"); + + let mut buf = tokio::io::ReadBuf::new(buf); + match std::pin::Pin::new(&mut self.tokio).poll_read(cx, &mut buf) { + std::task::Poll::Ready(Ok(_)) => Ok(buf.filled().len()), + std::task::Poll::Ready(Err(err)) => Err(err), + std::task::Poll::Pending => Err(std::io::ErrorKind::WouldBlock.into()), + } + } + } } -impl std::io::Write for TokioToStd where T: tokio::io::AsyncWrite + Unpin { - fn write(&mut self, buf: &[u8]) -> std::io::Result { - unsafe { - let cx = self.cx.as_mut().expect("TokioToStd used without setting task context"); - - match std::pin::Pin::new(&mut self.tokio).poll_write(cx, buf) { - std::task::Poll::Ready(result) => result, - std::task::Poll::Pending => Err(std::io::ErrorKind::WouldBlock.into()), - } - } - } - - fn flush(&mut self) -> std::io::Result<()> { - unsafe { - let cx = self.cx.as_mut().expect("TokioToStd used without setting task context"); - - match std::pin::Pin::new(&mut self.tokio).poll_flush(cx) { - std::task::Poll::Ready(result) => result, - std::task::Poll::Pending => Err(std::io::ErrorKind::WouldBlock.into()), - } - } - } +impl std::io::Write for TokioToStd +where + T: tokio::io::AsyncWrite + Unpin, +{ + fn write(&mut self, buf: &[u8]) -> std::io::Result { + unsafe { + let cx = self + .cx + .as_mut() + .expect("TokioToStd used without setting task context"); + + match std::pin::Pin::new(&mut self.tokio).poll_write(cx, buf) { + std::task::Poll::Ready(result) => result, + std::task::Poll::Pending => Err(std::io::ErrorKind::WouldBlock.into()), + } + } + } + + fn flush(&mut self) -> std::io::Result<()> { + unsafe { + let cx = self + .cx + .as_mut() + .expect("TokioToStd used without setting task context"); + + match std::pin::Pin::new(&mut self.tokio).poll_flush(cx) { + std::task::Poll::Ready(result) => result, + std::task::Poll::Pending => Err(std::io::ErrorKind::WouldBlock.into()), + } + } + } } diff --git a/edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/src/iotedge_client.rs b/edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/src/iotedge_client.rs index 60bb196f0aa..479a755c580 100644 --- a/edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/src/iotedge_client.rs +++ b/edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/src/iotedge_client.rs @@ -1,361 +1,425 @@ use std::future::Future; pub(crate) struct Client { - scheme: Scheme, - base: String, - inner: hyper::Client, + scheme: Scheme, + base: String, + inner: hyper::Client, } impl Client { - pub(crate) fn new(workload_url: &url::Url) -> Result { - let (scheme, base, connector) = - match workload_url.scheme() { - "http" => (Scheme::Http, workload_url.to_string(), Connector::Http(hyper::client::HttpConnector::new())), - "unix" => - if cfg!(windows) { - // We get better handling of Windows file syntax if we parse a - // unix:// URL as a file:// URL. Specifically: - // - On Unix, `Url::parse("unix:///path")?.to_file_path()` succeeds and returns `Ok("/path")`. - // - On Windows, `Url::parse("unix:///C:/path")?.to_file_path()` fails with `Err(())`. - // - On Windows, `Url::parse("file:///C:/path")?.to_file_path()` succeeds and returns `Ok(r"C:\path")`. - let mut workload_url = workload_url.clone(); - workload_url.set_scheme("file").expect(r#"changing the scheme of workload URI to "file" should not fail"#); - let base = workload_url.to_file_path().map_err(|()| Error::ParseWorkloadUrlUnixFilePath)?; - let base = base.to_str().ok_or(Error::ParseWorkloadUrlUnixFilePath)?; - (Scheme::Unix, base.to_owned(), Connector::Unix(hyper_uds::UdsConnector::new())) - } - else { - (Scheme::Unix, workload_url.path().to_owned(), Connector::Unix(hyper_uds::UdsConnector::new())) - }, - scheme => return Err(Error::UnrecognizedWorkloadUrlScheme(scheme.to_owned())), - }; - - let inner = hyper::Client::builder().build(connector); - - Ok(Client { - scheme, - base, - inner, - }) - } - - pub(crate) fn get_server_root_certificate(&self) -> impl Future, Error>> { - let url = - make_hyper_uri(self.scheme, &*self.base, "/trust-bundle?api-version=2019-01-30") - .map_err(|err| Error::GetServerRootCertificate(ApiErrorReason::ConstructRequestUrl(err))); - - let request = - url.and_then(|url| - http::Request::get(url).body(Default::default()) - .map_err(|err| Error::GetServerRootCertificate(ApiErrorReason::ConstructRequest(err)))); - - let response = request.map(|request| self.inner.request(request)); - - async { - use futures_util::StreamExt; - - let response = - response?.await - .map_err(|err| Error::GetServerRootCertificate(ApiErrorReason::ExecuteRequest(err)))?; - - let (response_parts, mut response_body) = response.into_parts(); - - let status = response_parts.status; - if status != http::StatusCode::OK { - return Err(Error::GetServerRootCertificate(ApiErrorReason::UnsuccessfulResponse(status))); - } - - let mut response = bytes::BytesMut::new(); - while let Some(chunk) = response_body.next().await { - let chunk = chunk.map_err(|err| Error::GetServerRootCertificate(ApiErrorReason::ReadResponse(err)))?; - response.extend_from_slice(&chunk); - } - - let TrustBundleResponse { certificate } = - serde_json::from_slice(&*response) - .map_err(|err| Error::GetServerRootCertificate(ApiErrorReason::ParseResponseBody(Box::new(err))))?; - - let mut server_root_certificate = vec![]; - - let mut current_cert = String::new(); - let mut lines = certificate.lines(); - - if lines.next() != Some("-----BEGIN CERTIFICATE-----") { - return Err(Error::GetServerRootCertificate(ApiErrorReason::ParseResponseBody("malformed PEM: does not start with BEGIN CERTIFICATE".into()))); - } - current_cert.push_str("-----BEGIN CERTIFICATE-----\n"); - - for line in lines { - - if line == "-----END CERTIFICATE-----" { - current_cert.push_str("\n-----END CERTIFICATE-----"); - let current_cert = std::mem::take(&mut current_cert); - let certificate = - native_tls::Certificate::from_pem(current_cert.as_bytes()) - .map_err(|err| Error::GetServerRootCertificate(ApiErrorReason::ParseResponseBody(Box::new(err))))?; - - server_root_certificate.push(certificate); - } - else if line == "-----BEGIN CERTIFICATE-----" { - if !current_cert.is_empty() { - return Err(Error::GetServerRootCertificate(ApiErrorReason::ParseResponseBody("malformed PEM: BEGIN CERTIFICATE without prior END CERTIFICATE".into()))); - } - current_cert.push_str("-----BEGIN CERTIFICATE-----\n"); - } - else { - current_cert.push_str(line); - } - } - if !current_cert.is_empty() { - return Err(Error::GetServerRootCertificate(ApiErrorReason::ParseResponseBody("malformed PEM: BEGIN CERTIFICATE without corresponding END CERTIFICATE".into()))); - } - - Ok(server_root_certificate) - } - } - - pub(crate) fn hmac_sha256(&self, module_id: &str, generation_id: &str, data: &str) -> impl Future> { - let url = - make_hyper_uri(self.scheme, &*self.base, &format!("/modules/{}/genid/{}/sign?api-version=2019-01-30", module_id, generation_id)) - .map_err(|err| Error::SignSasToken(ApiErrorReason::ConstructRequestUrl(err))); - - let request = - url.and_then(|url| { - let data = base64::encode(data.as_bytes()); - - let sign_request = SignRequest { - key_id: "primary", - algorithm: "HMACSHA256", - data: &data, - }; - - let body = serde_json::to_vec(&sign_request).map_err(|err| Error::SignSasToken(ApiErrorReason::SerializeRequestBody(err)))?; - - http::Request::post(url).body(body.into()) - .map_err(|err| Error::SignSasToken(ApiErrorReason::ConstructRequest(err))) - }); - - let response = request.map(|request| self.inner.request(request)); - - - async { - use futures_util::StreamExt; - - let response = - response?.await - .map_err(|err| Error::SignSasToken(ApiErrorReason::ExecuteRequest(err)))?; - - let (response_parts, mut response_body) = response.into_parts(); - - let status = response_parts.status; - if status != http::StatusCode::OK { - return Err(Error::SignSasToken(ApiErrorReason::UnsuccessfulResponse(status))); - } - - let mut response = bytes::BytesMut::new(); - while let Some(chunk) = response_body.next().await { - let chunk = chunk.map_err(|err| Error::SignSasToken(ApiErrorReason::ReadResponse(err)))?; - response.extend_from_slice(&chunk); - } - - let SignResponse { digest } = - serde_json::from_slice(&*response) - .map_err(|err| Error::SignSasToken(ApiErrorReason::ParseResponseBody(Box::new(err))))?; - - Ok(digest) - } - } + pub(crate) fn new(workload_url: &url::Url) -> Result { + let (scheme, base, connector) = match workload_url.scheme() { + "http" => ( + Scheme::Http, + workload_url.to_string(), + Connector::Http(hyper::client::HttpConnector::new()), + ), + "unix" => ( + Scheme::Unix, + workload_url.path().to_owned(), + Connector::Unix(hyperlocal::UnixConnector), + ), + scheme => return Err(Error::UnrecognizedWorkloadUrlScheme(scheme.to_owned())), + }; + + let inner = hyper::Client::builder().build(connector); + + Ok(Client { + scheme, + base, + inner, + }) + } + + pub(crate) fn get_server_root_certificate( + &self, + ) -> impl Future, Error>> { + let url = make_hyper_uri( + self.scheme, + &*self.base, + "/trust-bundle?api-version=2019-01-30", + ) + .map_err(|err| Error::GetServerRootCertificate(ApiErrorReason::ConstructRequestUrl(err))); + + let request = url.and_then(|url| { + http::Request::get(url) + .body(Default::default()) + .map_err(|err| { + Error::GetServerRootCertificate(ApiErrorReason::ConstructRequest(err)) + }) + }); + + let response = request.map(|request| self.inner.request(request)); + + async { + use futures_util::StreamExt; + + let response = response?.await.map_err(|err| { + Error::GetServerRootCertificate(ApiErrorReason::ExecuteRequest(err)) + })?; + + let (response_parts, mut response_body) = response.into_parts(); + + let status = response_parts.status; + if status != http::StatusCode::OK { + return Err(Error::GetServerRootCertificate( + ApiErrorReason::UnsuccessfulResponse(status), + )); + } + + let mut response = bytes::BytesMut::new(); + while let Some(chunk) = response_body.next().await { + let chunk = chunk.map_err(|err| { + Error::GetServerRootCertificate(ApiErrorReason::ReadResponse(err)) + })?; + response.extend_from_slice(&chunk); + } + + let TrustBundleResponse { certificate } = + serde_json::from_slice(&*response).map_err(|err| { + Error::GetServerRootCertificate(ApiErrorReason::ParseResponseBody(Box::new( + err, + ))) + })?; + + let mut server_root_certificate = vec![]; + + let mut current_cert = String::new(); + let mut lines = certificate.lines(); + + if lines.next() != Some("-----BEGIN CERTIFICATE-----") { + return Err(Error::GetServerRootCertificate( + ApiErrorReason::ParseResponseBody( + "malformed PEM: does not start with BEGIN CERTIFICATE".into(), + ), + )); + } + current_cert.push_str("-----BEGIN CERTIFICATE-----\n"); + + for line in lines { + if line == "-----END CERTIFICATE-----" { + current_cert.push_str("\n-----END CERTIFICATE-----"); + let current_cert = std::mem::take(&mut current_cert); + let certificate = native_tls::Certificate::from_pem(current_cert.as_bytes()) + .map_err(|err| { + Error::GetServerRootCertificate(ApiErrorReason::ParseResponseBody( + Box::new(err), + )) + })?; + + server_root_certificate.push(certificate); + } else if line == "-----BEGIN CERTIFICATE-----" { + if !current_cert.is_empty() { + return Err(Error::GetServerRootCertificate( + ApiErrorReason::ParseResponseBody( + "malformed PEM: BEGIN CERTIFICATE without prior END CERTIFICATE" + .into(), + ), + )); + } + current_cert.push_str("-----BEGIN CERTIFICATE-----\n"); + } else { + current_cert.push_str(line); + } + } + if !current_cert.is_empty() { + return Err(Error::GetServerRootCertificate( + ApiErrorReason::ParseResponseBody( + "malformed PEM: BEGIN CERTIFICATE without corresponding END CERTIFICATE" + .into(), + ), + )); + } + + Ok(server_root_certificate) + } + } + + pub(crate) fn hmac_sha256( + &self, + module_id: &str, + generation_id: &str, + data: &str, + ) -> impl Future> { + let url = make_hyper_uri( + self.scheme, + &*self.base, + &format!( + "/modules/{}/genid/{}/sign?api-version=2019-01-30", + module_id, generation_id + ), + ) + .map_err(|err| Error::SignSasToken(ApiErrorReason::ConstructRequestUrl(err))); + + let request = url.and_then(|url| { + let data = base64::encode(data.as_bytes()); + + let sign_request = SignRequest { + key_id: "primary", + algorithm: "HMACSHA256", + data: &data, + }; + + let body = serde_json::to_vec(&sign_request) + .map_err(|err| Error::SignSasToken(ApiErrorReason::SerializeRequestBody(err)))?; + + http::Request::post(url) + .body(body.into()) + .map_err(|err| Error::SignSasToken(ApiErrorReason::ConstructRequest(err))) + }); + + let response = request.map(|request| self.inner.request(request)); + + async { + use futures_util::StreamExt; + + let response = response? + .await + .map_err(|err| Error::SignSasToken(ApiErrorReason::ExecuteRequest(err)))?; + + let (response_parts, mut response_body) = response.into_parts(); + + let status = response_parts.status; + if status != http::StatusCode::OK { + return Err(Error::SignSasToken(ApiErrorReason::UnsuccessfulResponse( + status, + ))); + } + + let mut response = bytes::BytesMut::new(); + while let Some(chunk) = response_body.next().await { + let chunk = + chunk.map_err(|err| Error::SignSasToken(ApiErrorReason::ReadResponse(err)))?; + response.extend_from_slice(&chunk); + } + + let SignResponse { digest } = serde_json::from_slice(&*response).map_err(|err| { + Error::SignSasToken(ApiErrorReason::ParseResponseBody(Box::new(err))) + })?; + + Ok(digest) + } + } } #[derive(Clone, Copy, Debug)] enum Scheme { - Http, - Unix, + Http, + Unix, } -fn make_hyper_uri(scheme: Scheme, base: &str, path: &str) -> Result> { - match scheme { - Scheme::Http => { - let base = url::Url::parse(base)?; - let url = base.join(path)?; - let url = url.as_str().parse()?; - Ok(url) - }, - - Scheme::Unix => Ok(hyper_uds::make_hyper_uri(base, path)?), - } +fn make_hyper_uri( + scheme: Scheme, + base: &str, + path: &str, +) -> Result> { + match scheme { + Scheme::Http => { + let base = url::Url::parse(base)?; + let url = base.join(path)?; + let url = url.as_str().parse()?; + Ok(url) + } + + Scheme::Unix => Ok(hyperlocal::Uri::new(base, path).into()), + } } #[derive(Clone)] enum Connector { - Http(hyper::client::HttpConnector), - Unix(hyper_uds::UdsConnector), + Http(hyper::client::HttpConnector), + Unix(hyperlocal::UnixConnector), } impl hyper::service::Service for Connector { - type Response = Transport; - type Error = std::io::Error; - type Future = Box> + Send + Unpin>; - - fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> std::task::Poll> { - match self { - Connector::Http(connector) => connector.poll_ready(cx).map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err)), - Connector::Unix(connector) => connector.poll_ready(cx), - } - } - - fn call(&mut self, req: http::Uri) -> Self::Future { - use futures_util::{ FutureExt, TryFutureExt }; - - match self { - Connector::Http(connector) => Box::new(connector.call(req).map(|transport| match transport { - Ok(transport) => Ok(Transport::Http(transport)), - Err(err) => Err(std::io::Error::new(std::io::ErrorKind::Other, err)), - })) as Self::Future, - Connector::Unix(connector) => Box::new(connector.call(req).map_ok(Transport::Unix)), - } - } + type Response = Transport; + type Error = std::io::Error; + type Future = Box> + Send + Unpin>; + + fn poll_ready( + &mut self, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + match self { + Connector::Http(connector) => connector + .poll_ready(cx) + .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err)), + Connector::Unix(connector) => connector.poll_ready(cx), + } + } + + fn call(&mut self, req: http::Uri) -> Self::Future { + use futures_util::{FutureExt, TryFutureExt}; + + match self { + Connector::Http(connector) => { + Box::new(connector.call(req).map(|transport| match transport { + Ok(transport) => Ok(Transport::Http(transport)), + Err(err) => Err(std::io::Error::new(std::io::ErrorKind::Other, err)), + })) as Self::Future + } + Connector::Unix(connector) => Box::new(connector.call(req).map_ok(Transport::Unix)), + } + } } enum Transport { - Http(>::Response), - Unix(>::Response), + Http(>::Response), + Unix(>::Response), } impl hyper::client::connect::Connection for Transport { - fn connected(&self) -> hyper::client::connect::Connected { - match self { - Transport::Http(transport) => transport.connected(), - Transport::Unix(transport) => transport.connected(), - } - } + fn connected(&self) -> hyper::client::connect::Connected { + match self { + Transport::Http(transport) => transport.connected(), + Transport::Unix(transport) => transport.connected(), + } + } } impl tokio::io::AsyncRead for Transport { - fn poll_read(mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, buf: &mut [u8]) -> std::task::Poll> { - match &mut *self { - Transport::Http(transport) => std::pin::Pin::new(transport).poll_read(cx, buf), - Transport::Unix(transport) => std::pin::Pin::new(transport).poll_read(cx, buf), - } - } - - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [std::mem::MaybeUninit]) -> bool { - match self { - Transport::Http(transport) => transport.prepare_uninitialized_buffer(buf), - Transport::Unix(transport) => transport.prepare_uninitialized_buffer(buf), - } - } + fn poll_read( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut tokio::io::ReadBuf<'_>, + ) -> std::task::Poll> { + match &mut *self { + Transport::Http(transport) => std::pin::Pin::new(transport).poll_read(cx, buf), + Transport::Unix(transport) => std::pin::Pin::new(transport).poll_read(cx, buf), + } + } } impl tokio::io::AsyncWrite for Transport { - fn poll_write(mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, buf: &[u8]) -> std::task::Poll> { - match &mut *self { - Transport::Http(transport) => std::pin::Pin::new(transport).poll_write(cx, buf), - Transport::Unix(transport) => std::pin::Pin::new(transport).poll_write(cx, buf), - } - } - - fn poll_flush(mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll> { - match &mut *self { - Transport::Http(transport) => std::pin::Pin::new(transport).poll_flush(cx), - Transport::Unix(transport) => std::pin::Pin::new(transport).poll_flush(cx), - } - } - - fn poll_shutdown(mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll> { - match &mut *self { - Transport::Http(transport) => std::pin::Pin::new(transport).poll_shutdown(cx), - Transport::Unix(transport) => std::pin::Pin::new(transport).poll_shutdown(cx), - } - } + fn poll_write( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + match &mut *self { + Transport::Http(transport) => std::pin::Pin::new(transport).poll_write(cx, buf), + Transport::Unix(transport) => std::pin::Pin::new(transport).poll_write(cx, buf), + } + } + + fn poll_flush( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + match &mut *self { + Transport::Http(transport) => std::pin::Pin::new(transport).poll_flush(cx), + Transport::Unix(transport) => std::pin::Pin::new(transport).poll_flush(cx), + } + } + + fn poll_shutdown( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + match &mut *self { + Transport::Http(transport) => std::pin::Pin::new(transport).poll_shutdown(cx), + Transport::Unix(transport) => std::pin::Pin::new(transport).poll_shutdown(cx), + } + } } #[derive(serde_derive::Deserialize)] struct TrustBundleResponse { - certificate: String, + certificate: String, } #[derive(serde_derive::Serialize)] struct SignRequest<'a> { - #[serde(rename = "keyId")] - key_id: &'static str, - #[serde(rename = "algo")] - algorithm: &'static str, - data: &'a str, + #[serde(rename = "keyId")] + key_id: &'static str, + #[serde(rename = "algo")] + algorithm: &'static str, + data: &'a str, } #[derive(serde_derive::Deserialize)] struct SignResponse { - digest: String, + digest: String, } #[derive(Debug)] pub(super) enum Error { - GetServerRootCertificate(ApiErrorReason), - ParseWorkloadUrlUnixFilePath, - SignSasToken(ApiErrorReason), - UnrecognizedWorkloadUrlScheme(String), + GetServerRootCertificate(ApiErrorReason), + SignSasToken(ApiErrorReason), + UnrecognizedWorkloadUrlScheme(String), } impl std::fmt::Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Error::GetServerRootCertificate(reason) => write!(f, "could not get server root certificate: {}", reason), - Error::ParseWorkloadUrlUnixFilePath => write!(f, "could not parse workload URL as UDS file path"), - Error::SignSasToken(reason) => write!(f, "could not create SAS token: {}", reason), - Error::UnrecognizedWorkloadUrlScheme(scheme) => write!(f, "unrecognized scheme {:?}", scheme), - } - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Error::GetServerRootCertificate(reason) => { + write!(f, "could not get server root certificate: {}", reason) + } + Error::SignSasToken(reason) => write!(f, "could not create SAS token: {}", reason), + Error::UnrecognizedWorkloadUrlScheme(scheme) => { + write!(f, "unrecognized scheme {:?}", scheme) + } + } + } } impl std::error::Error for Error { - #[allow(clippy::match_same_arms)] - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - Error::GetServerRootCertificate(reason) => reason.source(), - Error::ParseWorkloadUrlUnixFilePath => None, - Error::SignSasToken(reason) => reason.source(), - Error::UnrecognizedWorkloadUrlScheme(_) => None, - } - } + #[allow(clippy::match_same_arms)] + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Error::GetServerRootCertificate(reason) => reason.source(), + Error::SignSasToken(reason) => reason.source(), + Error::UnrecognizedWorkloadUrlScheme(_) => None, + } + } } #[derive(Debug)] pub(super) enum ApiErrorReason { - ConstructRequestUrl(Box), - ConstructRequest(http::Error), - ExecuteRequest(hyper::Error), - ParseResponseBody(Box), - ReadResponse(hyper::Error), - SerializeRequestBody(serde_json::Error), - UnsuccessfulResponse(http::StatusCode), + ConstructRequestUrl(Box), + ConstructRequest(http::Error), + ExecuteRequest(hyper::Error), + ParseResponseBody(Box), + ReadResponse(hyper::Error), + SerializeRequestBody(serde_json::Error), + UnsuccessfulResponse(http::StatusCode), } impl std::fmt::Display for ApiErrorReason { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ApiErrorReason::ConstructRequestUrl(err) => write!(f, "could not construct request URL: {}", err), - ApiErrorReason::ConstructRequest(err) => write!(f, "could not construct request: {}", err), - ApiErrorReason::ExecuteRequest(err) => write!(f, "could not execute request: {}", err), - ApiErrorReason::ParseResponseBody(err) => write!(f, "could not deserialize response: {}", err), - ApiErrorReason::ReadResponse(err) => write!(f, "could not read response: {}", err), - ApiErrorReason::SerializeRequestBody(err) => write!(f, "could not serialize request: {}", err), - ApiErrorReason::UnsuccessfulResponse(status) => write!(f, "response has status code {}", status), - } - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ApiErrorReason::ConstructRequestUrl(err) => { + write!(f, "could not construct request URL: {}", err) + } + ApiErrorReason::ConstructRequest(err) => { + write!(f, "could not construct request: {}", err) + } + ApiErrorReason::ExecuteRequest(err) => write!(f, "could not execute request: {}", err), + ApiErrorReason::ParseResponseBody(err) => { + write!(f, "could not deserialize response: {}", err) + } + ApiErrorReason::ReadResponse(err) => write!(f, "could not read response: {}", err), + ApiErrorReason::SerializeRequestBody(err) => { + write!(f, "could not serialize request: {}", err) + } + ApiErrorReason::UnsuccessfulResponse(status) => { + write!(f, "response has status code {}", status) + } + } + } } impl std::error::Error for ApiErrorReason { - #[allow(clippy::match_same_arms)] - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - ApiErrorReason::ConstructRequestUrl(err) => Some(&**err), - ApiErrorReason::ConstructRequest(err) => Some(err), - ApiErrorReason::ExecuteRequest(err) => Some(err), - ApiErrorReason::ParseResponseBody(err) => Some(&**err), - ApiErrorReason::ReadResponse(err) => Some(err), - ApiErrorReason::SerializeRequestBody(err) => Some(err), - ApiErrorReason::UnsuccessfulResponse(_) => None, - } - } + #[allow(clippy::match_same_arms)] + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + ApiErrorReason::ConstructRequestUrl(err) => Some(&**err), + ApiErrorReason::ConstructRequest(err) => Some(err), + ApiErrorReason::ExecuteRequest(err) => Some(err), + ApiErrorReason::ParseResponseBody(err) => Some(&**err), + ApiErrorReason::ReadResponse(err) => Some(err), + ApiErrorReason::SerializeRequestBody(err) => Some(err), + ApiErrorReason::UnsuccessfulResponse(_) => None, + } + } } diff --git a/edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/src/twin_state/desired.rs b/edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/src/twin_state/desired.rs index 45b2e15a515..fd188ac145f 100644 --- a/edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/src/twin_state/desired.rs +++ b/edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/src/twin_state/desired.rs @@ -2,205 +2,231 @@ use std::future::Future; #[derive(Debug)] pub(crate) struct State { - max_back_off: std::time::Duration, - current_back_off: std::time::Duration, + max_back_off: std::time::Duration, + current_back_off: std::time::Duration, - keep_alive: std::time::Duration, + keep_alive: std::time::Duration, - inner: Inner, + inner: Inner, } enum Inner { - BeginBackOff, + BeginBackOff, - EndBackOff(tokio::time::Delay), + EndBackOff(std::pin::Pin>), - SendRequest, + SendRequest, - WaitingForResponse { - request_id: u8, - timeout: tokio::time::Delay, - }, + WaitingForResponse { + request_id: u8, + timeout: std::pin::Pin>, + }, - HaveResponse { - version: usize, - }, + HaveResponse { + version: usize, + }, } impl State { - pub(crate) fn new( - max_back_off: std::time::Duration, - keep_alive: std::time::Duration, - ) -> Self { - State { - max_back_off, - current_back_off: std::time::Duration::from_secs(0), + pub(crate) fn new(max_back_off: std::time::Duration, keep_alive: std::time::Duration) -> Self { + State { + max_back_off, + current_back_off: std::time::Duration::from_secs(0), - keep_alive, + keep_alive, - inner: Default::default(), - } - } + inner: Default::default(), + } + } - #[allow( + #[allow( clippy::unneeded_field_pattern, // Clippy wants wildcard pattern for the `if let Some(Response)` pattern below, // which would silently allow fields to be added to the variant without adding them here )] - pub(crate) fn poll( - &mut self, - cx: &mut std::task::Context<'_>, - - client: &mut mqtt3::Client, - - message: &mut Option, - previous_request_id: &mut u8, - ) -> Result, super::MessageParseError> { - loop { - log::trace!(" {:?}", self.inner); - - match &mut self.inner { - Inner::BeginBackOff => match self.current_back_off { - back_off if back_off.as_secs() == 0 => { - self.current_back_off = std::time::Duration::from_secs(1); - self.inner = Inner::SendRequest; - }, - - back_off => { - log::debug!("Backing off for {:?}", back_off); - self.current_back_off = std::cmp::min(self.max_back_off, self.current_back_off * 2); - self.inner = Inner::EndBackOff(tokio::time::delay_for(back_off)); - }, - }, - - Inner::EndBackOff(back_off_timer) => match std::pin::Pin::new(back_off_timer).poll(cx) { - std::task::Poll::Ready(()) => self.inner = Inner::SendRequest, - std::task::Poll::Pending => (), - }, - - Inner::SendRequest => { - let request_id = previous_request_id.wrapping_add(1); - *previous_request_id = request_id; - - // We don't care about the response since this is a QoS 0 publication. - // We don't even need to `poll()` the future because `mqtt3::Client::publish` puts it in the send queue *synchronously*. - // But we do need to tell the caller client to poll the `mqtt3::Client` at least once more so that it attempts to send the message, - // so return `Response::Continue`. - let _ = client.publish(mqtt3::proto::Publication { - topic_name: format!("$iothub/twin/GET/?$rid={}", request_id), - qos: mqtt3::proto::QoS::AtMostOnce, - retain: false, - payload: Default::default(), - }); - - let timeout = tokio::time::delay_for(2 * self.keep_alive); - self.inner = Inner::WaitingForResponse { request_id, timeout }; - return Ok(super::Response::Continue); - }, - - Inner::WaitingForResponse { request_id, timeout } => { - if let Some(super::InternalTwinStateMessage::Response { status, request_id: message_request_id, payload, version: _ }) = message { - if *message_request_id == *request_id { - match status { - crate::Status::Ok => { - let twin_state: crate::TwinState = serde_json::from_slice(payload).map_err(super::MessageParseError::Json)?; - - let _ = message.take(); - - self.inner = Inner::HaveResponse { version: twin_state.desired.version }; - return Ok(super::Response::Message(Message::Initial(twin_state))); - }, - - status @ crate::Status::TooManyRequests | - status @ crate::Status::Error(_) => { - log::warn!("getting initial twin state failed with status {}", status); - - let _ = message.take(); - - self.inner = Inner::BeginBackOff; - continue; - }, - - status => { - let status = *status; - let _ = message.take(); - return Err(super::MessageParseError::IotHubStatus(status)); - }, - } - } - } - - match std::pin::Pin::new(timeout).poll(cx) { - std::task::Poll::Ready(()) => { - log::warn!("timed out waiting for initial twin state response"); - self.inner = Inner::SendRequest; - }, - - std::task::Poll::Pending => return Ok(super::Response::NotReady), - } - }, - - Inner::HaveResponse { version } => match message.take() { - Some(super::InternalTwinStateMessage::TwinPatch(twin_properties)) => { - if twin_properties.version != *version + 1 { - log::warn!("expected PATCH response with version {} but received version {}", *version + 1, twin_properties.version); - self.inner = Inner::SendRequest; - continue; - } - - *version = twin_properties.version; - - return Ok(super::Response::Message(Message::Patch(twin_properties))); - }, - - other => { - *message = other; - return Ok(super::Response::NotReady); - }, - }, - } - } - } - - pub (crate) fn new_connection(&mut self) { - self.inner = Inner::SendRequest; - } + pub(crate) fn poll( + &mut self, + cx: &mut std::task::Context<'_>, + + client: &mut mqtt3::Client, + + message: &mut Option, + previous_request_id: &mut u8, + ) -> Result, super::MessageParseError> { + loop { + log::trace!(" {:?}", self.inner); + + match &mut self.inner { + Inner::BeginBackOff => match self.current_back_off { + back_off if back_off.as_secs() == 0 => { + self.current_back_off = std::time::Duration::from_secs(1); + self.inner = Inner::SendRequest; + } + + back_off => { + log::debug!("Backing off for {:?}", back_off); + self.current_back_off = + std::cmp::min(self.max_back_off, self.current_back_off * 2); + self.inner = Inner::EndBackOff(Box::pin(tokio::time::sleep(back_off))); + } + }, + + Inner::EndBackOff(back_off_timer) => { + match std::pin::Pin::new(back_off_timer).poll(cx) { + std::task::Poll::Ready(()) => self.inner = Inner::SendRequest, + std::task::Poll::Pending => (), + } + } + + Inner::SendRequest => { + let request_id = previous_request_id.wrapping_add(1); + *previous_request_id = request_id; + + // We don't care about the response since this is a QoS 0 publication. + // We don't even need to `poll()` the future because `mqtt3::Client::publish` puts it in the send queue *synchronously*. + // But we do need to tell the caller client to poll the `mqtt3::Client` at least once more so that it attempts to send the message, + // so return `Response::Continue`. + let _ = client.publish(mqtt3::proto::Publication { + topic_name: format!("$iothub/twin/GET/?$rid={}", request_id), + qos: mqtt3::proto::QoS::AtMostOnce, + retain: false, + payload: Default::default(), + }); + + let timeout = Box::pin(tokio::time::sleep(2 * self.keep_alive)); + self.inner = Inner::WaitingForResponse { + request_id, + timeout, + }; + return Ok(super::Response::Continue); + } + + Inner::WaitingForResponse { + request_id, + timeout, + } => { + if let Some(super::InternalTwinStateMessage::Response { + status, + request_id: message_request_id, + payload, + version: _, + }) = message + { + if *message_request_id == *request_id { + match status { + crate::Status::Ok => { + let twin_state: crate::TwinState = + serde_json::from_slice(payload) + .map_err(super::MessageParseError::Json)?; + + let _ = message.take(); + + self.inner = Inner::HaveResponse { + version: twin_state.desired.version, + }; + return Ok(super::Response::Message(Message::Initial( + twin_state, + ))); + } + + status @ crate::Status::TooManyRequests + | status @ crate::Status::Error(_) => { + log::warn!( + "getting initial twin state failed with status {}", + status + ); + + let _ = message.take(); + + self.inner = Inner::BeginBackOff; + continue; + } + + status => { + let status = *status; + let _ = message.take(); + return Err(super::MessageParseError::IotHubStatus(status)); + } + } + } + } + + match std::pin::Pin::new(timeout).poll(cx) { + std::task::Poll::Ready(()) => { + log::warn!("timed out waiting for initial twin state response"); + self.inner = Inner::SendRequest; + } + + std::task::Poll::Pending => return Ok(super::Response::NotReady), + } + } + + Inner::HaveResponse { version } => { + match message.take() { + Some(super::InternalTwinStateMessage::TwinPatch(twin_properties)) => { + if twin_properties.version != *version + 1 { + log::warn!("expected PATCH response with version {} but received version {}", *version + 1, twin_properties.version); + self.inner = Inner::SendRequest; + continue; + } + + *version = twin_properties.version; + + return Ok(super::Response::Message(Message::Patch(twin_properties))); + } + + other => { + *message = other; + return Ok(super::Response::NotReady); + } + } + } + } + } + } + + pub(crate) fn new_connection(&mut self) { + self.inner = Inner::SendRequest; + } } impl Default for Inner { - fn default() -> Self { - Inner::SendRequest - } + fn default() -> Self { + Inner::SendRequest + } } impl std::fmt::Debug for Inner { - #[allow( + #[allow( clippy::unneeded_field_pattern, // Clippy wants wildcard pattern for the WaitingForResponse arm, // which would silently allow fields to be added to the variant without adding them here )] - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Inner::BeginBackOff => f.debug_struct("BeginBackOff").finish(), - - Inner::EndBackOff(_) => f.debug_struct("EndBackOff").finish(), - - Inner::SendRequest => f.debug_struct("SendRequest").finish(), - - Inner::WaitingForResponse { request_id, timeout: _ } => - f.debug_struct("WaitingForResponse") - .field("request_id", request_id) - .finish(), - - Inner::HaveResponse { version } => - f.debug_struct("HaveResponse") - .field("version", version) - .finish(), - } - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Inner::BeginBackOff => f.debug_struct("BeginBackOff").finish(), + + Inner::EndBackOff(_) => f.debug_struct("EndBackOff").finish(), + + Inner::SendRequest => f.debug_struct("SendRequest").finish(), + + Inner::WaitingForResponse { + request_id, + timeout: _, + } => f + .debug_struct("WaitingForResponse") + .field("request_id", request_id) + .finish(), + + Inner::HaveResponse { version } => f + .debug_struct("HaveResponse") + .field("version", version) + .finish(), + } + } } #[derive(Debug)] pub(crate) enum Message { - Initial(crate::TwinState), + Initial(crate::TwinState), - Patch(crate::TwinProperties), + Patch(crate::TwinProperties), } diff --git a/edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/src/twin_state/reported.rs b/edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/src/twin_state/reported.rs index 156352f229b..a347d841ba4 100644 --- a/edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/src/twin_state/reported.rs +++ b/edge-modules/api-proxy-module/rust-sdk/azure-iot-mqtt/src/twin_state/reported.rs @@ -2,226 +2,249 @@ use std::future::Future; #[derive(Debug)] pub(crate) struct State { - max_back_off: std::time::Duration, - current_back_off: std::time::Duration, + max_back_off: std::time::Duration, + current_back_off: std::time::Duration, - keep_alive: std::time::Duration, + keep_alive: std::time::Duration, - report_twin_state_send: futures_channel::mpsc::Sender, - report_twin_state_recv: futures_channel::mpsc::Receiver, - previous_twin_state: Option>, - current_twin_state: std::collections::HashMap, - pending_response: Option<(u8, tokio::time::Delay)>, + report_twin_state_send: futures_channel::mpsc::Sender, + report_twin_state_recv: futures_channel::mpsc::Receiver, + previous_twin_state: Option>, + current_twin_state: std::collections::HashMap, + pending_response: Option<(u8, std::pin::Pin>)>, - inner: Inner, + inner: Inner, } enum Inner { - BeginBackOff, + BeginBackOff, - EndBackOff(tokio::time::Delay), + EndBackOff(std::pin::Pin>), - Idle, + Idle, - SendRequest, + SendRequest, } impl State { - pub(crate) fn new( - max_back_off: std::time::Duration, - keep_alive: std::time::Duration, - ) -> Self { - let (report_twin_state_send, report_twin_state_recv) = futures_channel::mpsc::channel(0); + pub(crate) fn new(max_back_off: std::time::Duration, keep_alive: std::time::Duration) -> Self { + let (report_twin_state_send, report_twin_state_recv) = futures_channel::mpsc::channel(0); - State { - max_back_off, - current_back_off: std::time::Duration::from_secs(0), + State { + max_back_off, + current_back_off: std::time::Duration::from_secs(0), - keep_alive, + keep_alive, - report_twin_state_send, - report_twin_state_recv, - previous_twin_state: None, - current_twin_state: Default::default(), - pending_response: None, + report_twin_state_send, + report_twin_state_recv, + previous_twin_state: None, + current_twin_state: Default::default(), + pending_response: None, - inner: Default::default(), - } - } + inner: Default::default(), + } + } - #[allow( + #[allow( clippy::unneeded_field_pattern, // Clippy wants wildcard pattern for the `if let Some(Response)` pattern below, // which would silently allow fields to be added to the variant without adding them here )] - pub(crate) fn poll( - &mut self, - cx: &mut std::task::Context<'_>, - - client: &mut mqtt3::Client, - - message: &mut Option, - previous_request_id: &mut u8, - ) -> Result, super::MessageParseError> { - use futures_core::Stream; - - loop { - log::trace!(" {:?}", self.inner); - - match &mut self.inner { - Inner::BeginBackOff => match self.current_back_off { - back_off if back_off.as_secs() == 0 => { - self.current_back_off = std::time::Duration::from_secs(1); - self.inner = Inner::SendRequest; - }, - - back_off => { - log::debug!("Backing off for {:?}", back_off); - self.current_back_off = std::cmp::min(self.max_back_off, self.current_back_off * 2); - self.inner = Inner::EndBackOff(tokio::time::delay_for(back_off)); - }, - }, - - Inner::EndBackOff(back_off_timer) => match std::pin::Pin::new(back_off_timer).poll(cx) { - std::task::Poll::Ready(()) => self.inner = Inner::SendRequest, - std::task::Poll::Pending => (), - }, - - Inner::Idle => { - let mut current_twin_state_changed = false; - - while let std::task::Poll::Ready(Some(report_twin_state_request)) = std::pin::Pin::new(&mut self.report_twin_state_recv).poll_next(cx) { - match report_twin_state_request { - ReportTwinStateRequest::Replace(properties) => self.current_twin_state = properties, - ReportTwinStateRequest::Patch(patch) => merge(&mut self.current_twin_state, patch), - } - - current_twin_state_changed = true; - } - - if current_twin_state_changed && self.previous_twin_state.as_ref() != Some(&self.current_twin_state) { - self.inner = Inner::SendRequest; - continue; - } - - if let Some((request_id, timeout)) = &mut self.pending_response { - if let Some(super::InternalTwinStateMessage::Response { status, request_id: message_request_id, payload: _, version }) = message { - if *message_request_id == *request_id { - match status { - crate::Status::Ok | - crate::Status::NoContent => { - let version = *version; - - let _ = message.take(); - - self.previous_twin_state = Some(self.current_twin_state.clone()); - self.pending_response = None; - - return Ok(super::Response::Message(Message::Reported(version))); - }, - - status @ crate::Status::TooManyRequests | - status @ crate::Status::Error(_) => { - log::warn!("reporting twin state failed with status {}", status); - - let _ = message.take(); - - self.inner = Inner::BeginBackOff; - continue; - }, - - status => { - let status = *status; - let _ = message.take(); - return Err(super::MessageParseError::IotHubStatus(status)); - }, - } - } - } - - match std::pin::Pin::new(timeout).poll(cx) { - std::task::Poll::Ready(()) => { - log::warn!("timed out waiting for report twin state response"); - self.inner = Inner::SendRequest; - }, - - std::task::Poll::Pending => return Ok(super::Response::NotReady), - } - } - else { - return Ok(super::Response::NotReady); - } - }, - - Inner::SendRequest => { - let patch = - if let Some(previous_twin_state) = &self.previous_twin_state { - diff(&previous_twin_state, &self.current_twin_state) - } - else { - // Wait for desired_properties to provide the initial reported twin state - return Ok(super::Response::NotReady); - }; - let payload = serde_json::to_vec(&patch).expect("cannot fail to serialize HashMap"); - - let request_id = previous_request_id.wrapping_add(1); - *previous_request_id = request_id; - - // We don't care about the response since this is a QoS 0 publication. - // We don't even need to `poll()` the future because `mqtt3::Client::publish` puts it in the send queue *synchronously*. - // But we do need to tell the caller client to poll the `mqtt3::Client` at least once more so that it attempts to send the message, - // so return `Response::Continue`. - let _ = client.publish(mqtt3::proto::Publication { - topic_name: format!("$iothub/twin/PATCH/properties/reported/?$rid={}", request_id), - qos: mqtt3::proto::QoS::AtMostOnce, - retain: false, - payload: payload.into(), - }); - - let timeout = tokio::time::delay_for(2 * self.keep_alive); - - self.pending_response = Some((request_id, timeout)); - - self.inner = Inner::Idle; - - return Ok(super::Response::Continue); - }, - } - } - } - - pub (crate) fn new_connection(&mut self) { - self.previous_twin_state = None; - self.inner = Inner::SendRequest; - } - - pub(crate) fn set_initial_state(&mut self, state: std::collections::HashMap) { - self.previous_twin_state = Some(state); - self.inner = Inner::SendRequest; - } - - pub(crate) fn report_twin_state_handle(&self) -> ReportTwinStateHandle { - ReportTwinStateHandle(self.report_twin_state_send.clone()) - } + pub(crate) fn poll( + &mut self, + cx: &mut std::task::Context<'_>, + + client: &mut mqtt3::Client, + + message: &mut Option, + previous_request_id: &mut u8, + ) -> Result, super::MessageParseError> { + use futures_core::Stream; + + loop { + log::trace!(" {:?}", self.inner); + + match &mut self.inner { + Inner::BeginBackOff => match self.current_back_off { + back_off if back_off.as_secs() == 0 => { + self.current_back_off = std::time::Duration::from_secs(1); + self.inner = Inner::SendRequest; + } + + back_off => { + log::debug!("Backing off for {:?}", back_off); + self.current_back_off = + std::cmp::min(self.max_back_off, self.current_back_off * 2); + self.inner = Inner::EndBackOff(Box::pin(tokio::time::sleep(back_off))); + } + }, + + Inner::EndBackOff(back_off_timer) => { + match std::pin::Pin::new(back_off_timer).poll(cx) { + std::task::Poll::Ready(()) => self.inner = Inner::SendRequest, + std::task::Poll::Pending => (), + } + } + + Inner::Idle => { + let mut current_twin_state_changed = false; + + while let std::task::Poll::Ready(Some(report_twin_state_request)) = + std::pin::Pin::new(&mut self.report_twin_state_recv).poll_next(cx) + { + match report_twin_state_request { + ReportTwinStateRequest::Replace(properties) => { + self.current_twin_state = properties + } + ReportTwinStateRequest::Patch(patch) => { + merge(&mut self.current_twin_state, patch) + } + } + + current_twin_state_changed = true; + } + + if current_twin_state_changed + && self.previous_twin_state.as_ref() != Some(&self.current_twin_state) + { + self.inner = Inner::SendRequest; + continue; + } + + if let Some((request_id, timeout)) = &mut self.pending_response { + if let Some(super::InternalTwinStateMessage::Response { + status, + request_id: message_request_id, + payload: _, + version, + }) = message + { + if *message_request_id == *request_id { + match status { + crate::Status::Ok | crate::Status::NoContent => { + let version = *version; + + let _ = message.take(); + + self.previous_twin_state = + Some(self.current_twin_state.clone()); + self.pending_response = None; + + return Ok(super::Response::Message(Message::Reported( + version, + ))); + } + + status @ crate::Status::TooManyRequests + | status @ crate::Status::Error(_) => { + log::warn!( + "reporting twin state failed with status {}", + status + ); + + let _ = message.take(); + + self.inner = Inner::BeginBackOff; + continue; + } + + status => { + let status = *status; + let _ = message.take(); + return Err(super::MessageParseError::IotHubStatus(status)); + } + } + } + } + + match std::pin::Pin::new(timeout).poll(cx) { + std::task::Poll::Ready(()) => { + log::warn!("timed out waiting for report twin state response"); + self.inner = Inner::SendRequest; + } + + std::task::Poll::Pending => return Ok(super::Response::NotReady), + } + } else { + return Ok(super::Response::NotReady); + } + } + + Inner::SendRequest => { + let patch = if let Some(previous_twin_state) = &self.previous_twin_state { + diff(&previous_twin_state, &self.current_twin_state) + } else { + // Wait for desired_properties to provide the initial reported twin state + return Ok(super::Response::NotReady); + }; + let payload = serde_json::to_vec(&patch) + .expect("cannot fail to serialize HashMap"); + + let request_id = previous_request_id.wrapping_add(1); + *previous_request_id = request_id; + + // We don't care about the response since this is a QoS 0 publication. + // We don't even need to `poll()` the future because `mqtt3::Client::publish` puts it in the send queue *synchronously*. + // But we do need to tell the caller client to poll the `mqtt3::Client` at least once more so that it attempts to send the message, + // so return `Response::Continue`. + let _ = client.publish(mqtt3::proto::Publication { + topic_name: format!( + "$iothub/twin/PATCH/properties/reported/?$rid={}", + request_id + ), + qos: mqtt3::proto::QoS::AtMostOnce, + retain: false, + payload: payload.into(), + }); + + let timeout = Box::pin(tokio::time::sleep(2 * self.keep_alive)); + + self.pending_response = Some((request_id, timeout)); + + self.inner = Inner::Idle; + + return Ok(super::Response::Continue); + } + } + } + } + + pub(crate) fn new_connection(&mut self) { + self.previous_twin_state = None; + self.inner = Inner::SendRequest; + } + + pub(crate) fn set_initial_state( + &mut self, + state: std::collections::HashMap, + ) { + self.previous_twin_state = Some(state); + self.inner = Inner::SendRequest; + } + + pub(crate) fn report_twin_state_handle(&self) -> ReportTwinStateHandle { + ReportTwinStateHandle(self.report_twin_state_send.clone()) + } } impl Default for Inner { - fn default() -> Self { - Inner::Idle - } + fn default() -> Self { + Inner::Idle + } } impl std::fmt::Debug for Inner { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Inner::BeginBackOff => f.debug_struct("BeginBackOff").finish(), + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Inner::BeginBackOff => f.debug_struct("BeginBackOff").finish(), - Inner::EndBackOff(_) => f.debug_struct("EndBackOff").finish(), + Inner::EndBackOff(_) => f.debug_struct("EndBackOff").finish(), - Inner::Idle => f.debug_struct("Idle").finish(), + Inner::Idle => f.debug_struct("Idle").finish(), - Inner::SendRequest => f.debug_struct("SendRequest").finish(), - } - } + Inner::SendRequest => f.debug_struct("SendRequest").finish(), + } + } } /// Used to report twin state to the Azure IoT Hub @@ -229,216 +252,221 @@ impl std::fmt::Debug for Inner { pub struct ReportTwinStateHandle(futures_channel::mpsc::Sender); impl ReportTwinStateHandle { - /// Send a direct method response with the given parameters - pub async fn report_twin_state(&mut self, request: ReportTwinStateRequest) -> Result<(), ReportTwinStateError> { - use futures_util::SinkExt; - - self.0.send(request).await.map_err(|_| ReportTwinStateError::ClientDoesNotExist) - } + /// Send a direct method response with the given parameters + pub async fn report_twin_state( + &mut self, + request: ReportTwinStateRequest, + ) -> Result<(), ReportTwinStateError> { + use futures_util::SinkExt; + + self.0 + .send(request) + .await + .map_err(|_| ReportTwinStateError::ClientDoesNotExist) + } } /// The kind of twin state update #[derive(Debug)] pub enum ReportTwinStateRequest { - Replace(std::collections::HashMap), - Patch(std::collections::HashMap), + Replace(std::collections::HashMap), + Patch(std::collections::HashMap), } #[derive(Debug)] pub enum ReportTwinStateError { - ClientDoesNotExist, + ClientDoesNotExist, } impl std::fmt::Display for ReportTwinStateError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ReportTwinStateError::ClientDoesNotExist => write!(f, "client does not exist"), - } - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ReportTwinStateError::ClientDoesNotExist => write!(f, "client does not exist"), + } + } } -impl std::error::Error for ReportTwinStateError { -} +impl std::error::Error for ReportTwinStateError {} #[derive(Debug)] pub(crate) enum Message { - Reported(Option), + Reported(Option), } -fn merge(properties: &mut std::collections::HashMap, patch: std::collections::HashMap) { - fn merge_inner(original_value: &mut serde_json::Value, patch: serde_json::Value) { - if let serde_json::Value::Object(original_value) = original_value { - if let serde_json::Value::Object(patch) = patch { - for (key, value) in patch { - if value.is_null() { - original_value.remove(&key); - } - else { - merge_inner(original_value.entry(key).or_insert(serde_json::Value::Null), value); - } - } - - return; - } - } - - *original_value = patch; - } - - for (key, value) in patch { - if value.is_null() { - properties.remove(&key); - } - else { - merge_inner(properties.entry(key).or_insert(serde_json::Value::Null), value); - } - } +fn merge( + properties: &mut std::collections::HashMap, + patch: std::collections::HashMap, +) { + fn merge_inner(original_value: &mut serde_json::Value, patch: serde_json::Value) { + if let serde_json::Value::Object(original_value) = original_value { + if let serde_json::Value::Object(patch) = patch { + for (key, value) in patch { + if value.is_null() { + original_value.remove(&key); + } else { + merge_inner( + original_value.entry(key).or_insert(serde_json::Value::Null), + value, + ); + } + } + + return; + } + } + + *original_value = patch; + } + + for (key, value) in patch { + if value.is_null() { + properties.remove(&key); + } else { + merge_inner( + properties.entry(key).or_insert(serde_json::Value::Null), + value, + ); + } + } } fn diff( - previous: &std::collections::HashMap, - current: &std::collections::HashMap, + previous: &std::collections::HashMap, + current: &std::collections::HashMap, ) -> std::collections::HashMap { - fn diff_inner(previous: &serde_json::Value, current: &serde_json::Value) -> serde_json::Value { - match (previous, current) { - (serde_json::Value::Object(previous), serde_json::Value::Object(current)) => { - let mut result: serde_json::Map<_, _> = Default::default(); - - for (key, previous_value) in previous { - if let Some(current_value) = current.get(key) { - if previous_value != current_value { - result.insert(key.clone(), diff_inner(previous_value, current_value)); - } - } - else { - result.insert(key.clone(), serde_json::Value::Null); - } - } - - for (key, current_value) in current { - if !previous.contains_key(key) { - result.insert(key.clone(), current_value.clone()); - } - } - - serde_json::Value::Object(result) - }, - - (_, _) => current.clone() - } - } - - let mut result: std::collections::HashMap<_, _> = Default::default(); - - for (key, previous_value) in previous { - if let Some(current_value) = current.get(key) { - if previous_value != current_value { - result.insert(key.clone(), diff_inner(previous_value, current_value)); - } - } - else { - result.insert(key.clone(), serde_json::Value::Null); - } - } - - for (key, current_value) in current { - if !previous.contains_key(key) { - result.insert(key.clone(), current_value.clone()); - } - } - - result + fn diff_inner(previous: &serde_json::Value, current: &serde_json::Value) -> serde_json::Value { + match (previous, current) { + (serde_json::Value::Object(previous), serde_json::Value::Object(current)) => { + let mut result: serde_json::Map<_, _> = Default::default(); + + for (key, previous_value) in previous { + if let Some(current_value) = current.get(key) { + if previous_value != current_value { + result.insert(key.clone(), diff_inner(previous_value, current_value)); + } + } else { + result.insert(key.clone(), serde_json::Value::Null); + } + } + + for (key, current_value) in current { + if !previous.contains_key(key) { + result.insert(key.clone(), current_value.clone()); + } + } + + serde_json::Value::Object(result) + } + + (_, _) => current.clone(), + } + } + + let mut result: std::collections::HashMap<_, _> = Default::default(); + + for (key, previous_value) in previous { + if let Some(current_value) = current.get(key) { + if previous_value != current_value { + result.insert(key.clone(), diff_inner(previous_value, current_value)); + } + } else { + result.insert(key.clone(), serde_json::Value::Null); + } + } + + for (key, current_value) in current { + if !previous.contains_key(key) { + result.insert(key.clone(), current_value.clone()); + } + } + + result } #[cfg(test)] mod tests { - #[test] - fn diff_merge() { - verify_diff_merge( - serde_json::json!({ - "key1": "value1", - "key2": ["value2"], - "key3": { - "key3.1": "value3.1", - "key3.2": "value3.2" - }, - "key4": { - "key4.1": 5 - } - }), - - serde_json::json!({ - "key1": "new_value1", - "key2": ["new_value2.1", "new_value2.2"], - "key3": { - "key3.1": "new_value3.1", - "key3.3": "new_value3.3" - }, - "key4": "new_value4" - }), - - serde_json::json!({ - "key1": "new_value1", - "key2": ["new_value2.1", "new_value2.2"], - "key3": { - "key3.1": "new_value3.1", - "key3.2": "value3.2", - "key3.3": "new_value3.3" - }, - "key4": "new_value4" - }), - ); - - verify_diff_merge( - serde_json::json!({ - "key1": "value1", - "key2": ["value2"], - "key3": { - "key3.1": "value3.1", - "key3.2": "value3.2" - } - }), - - serde_json::json!({ - "key1": null, - "key2": null, - "key3": null - }), - - serde_json::json!({ - }), - ); - } - - fn verify_diff_merge(previous: serde_json::Value, patch: serde_json::Value, current: serde_json::Value) { - let mut previous: std::collections::HashMap<_, _> = - if let serde_json::Value::Object(map) = previous { - map.into_iter().collect() - } - else { - panic!("previous should be a map"); - }; - - let patch: std::collections::HashMap<_, _> = - if let serde_json::Value::Object(map) = patch { - map.into_iter().collect() - } - else { - panic!("patch should be a map"); - }; - - let current: std::collections::HashMap<_, _> = - if let serde_json::Value::Object(map) = current { - map.into_iter().collect() - } - else { - panic!("current should be a map"); - }; - - let actual_patch = super::diff(&previous, ¤t); - assert_eq!(patch, actual_patch); - - super::merge(&mut previous, patch); - assert_eq!(previous, current); - } + #[test] + fn diff_merge() { + verify_diff_merge( + serde_json::json!({ + "key1": "value1", + "key2": ["value2"], + "key3": { + "key3.1": "value3.1", + "key3.2": "value3.2" + }, + "key4": { + "key4.1": 5 + } + }), + serde_json::json!({ + "key1": "new_value1", + "key2": ["new_value2.1", "new_value2.2"], + "key3": { + "key3.1": "new_value3.1", + "key3.3": "new_value3.3" + }, + "key4": "new_value4" + }), + serde_json::json!({ + "key1": "new_value1", + "key2": ["new_value2.1", "new_value2.2"], + "key3": { + "key3.1": "new_value3.1", + "key3.2": "value3.2", + "key3.3": "new_value3.3" + }, + "key4": "new_value4" + }), + ); + + verify_diff_merge( + serde_json::json!({ + "key1": "value1", + "key2": ["value2"], + "key3": { + "key3.1": "value3.1", + "key3.2": "value3.2" + } + }), + serde_json::json!({ + "key1": null, + "key2": null, + "key3": null + }), + serde_json::json!({}), + ); + } + + fn verify_diff_merge( + previous: serde_json::Value, + patch: serde_json::Value, + current: serde_json::Value, + ) { + let mut previous: std::collections::HashMap<_, _> = + if let serde_json::Value::Object(map) = previous { + map.into_iter().collect() + } else { + panic!("previous should be a map"); + }; + + let patch: std::collections::HashMap<_, _> = if let serde_json::Value::Object(map) = patch { + map.into_iter().collect() + } else { + panic!("patch should be a map"); + }; + + let current: std::collections::HashMap<_, _> = + if let serde_json::Value::Object(map) = current { + map.into_iter().collect() + } else { + panic!("current should be a map"); + }; + + let actual_patch = super::diff(&previous, ¤t); + assert_eq!(patch, actual_patch); + + super::merge(&mut previous, patch); + assert_eq!(previous, current); + } } diff --git a/edge-modules/api-proxy-module/rust-sdk/hyper-uds/Cargo.toml b/edge-modules/api-proxy-module/rust-sdk/hyper-uds/Cargo.toml deleted file mode 100644 index 7c484a5566e..00000000000 --- a/edge-modules/api-proxy-module/rust-sdk/hyper-uds/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "hyper-uds" -version = "0.1.0" -authors = ["Azure IoT Edge Devs"] -license = "MIT" -edition = "2018" - -[dependencies] -bytes = "0.5" -futures = "0.3" -hex = "0.4" -hyper = "0.13" -tokio = { version = "0.2", features = ["blocking", "io-util", "macros", "sync"] } diff --git a/edge-modules/api-proxy-module/rust-sdk/hyper-uds/LICENSE b/edge-modules/api-proxy-module/rust-sdk/hyper-uds/LICENSE deleted file mode 100644 index 5cf7c8db628..00000000000 --- a/edge-modules/api-proxy-module/rust-sdk/hyper-uds/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) Microsoft Corporation. All rights reserved. - -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 diff --git a/edge-modules/api-proxy-module/rust-sdk/hyper-uds/README.md b/edge-modules/api-proxy-module/rust-sdk/hyper-uds/README.md deleted file mode 100644 index 05b9bb54fa8..00000000000 --- a/edge-modules/api-proxy-module/rust-sdk/hyper-uds/README.md +++ /dev/null @@ -1,8 +0,0 @@ -An implementation of `hyper::service::Service` (for clients) and `hyper::server::accept::Accept` (for servers) that uses Unix Domain Sockets. - -This is similar to [the `hyperlocal` crate](https://crates.io/crates/hyperlocal) but supports both Linux and Windows. - - -# License - -MIT diff --git a/edge-modules/api-proxy-module/rust-sdk/hyper-uds/src/lib.rs b/edge-modules/api-proxy-module/rust-sdk/hyper-uds/src/lib.rs deleted file mode 100644 index 2cdf8b5bee3..00000000000 --- a/edge-modules/api-proxy-module/rust-sdk/hyper-uds/src/lib.rs +++ /dev/null @@ -1,322 +0,0 @@ -#![deny(rust_2018_idioms, warnings)] -#![deny(clippy::all, clippy::pedantic)] -#![allow( - clippy::default_trait_access, - clippy::missing_errors_doc, - clippy::must_use_candidate, - clippy::too_many_lines, - clippy::type_complexity, -)] - -pub struct UnixStream { - pending_read: std::sync::Arc>, - reader_sender: std::sync::mpsc::Sender, - - pending_write: std::sync::Arc>, - flush_sender: std::sync::mpsc::Sender, -} - -struct PendingRead { - bytes: bytes::BytesMut, - err: Option, - eof: bool, -} - -struct PendingWrite { - bytes: bytes::BytesMut, - flushed: bool, - err: Option, - eof: bool, -} - -impl std::fmt::Debug for UnixStream { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("UnixStream").finish() - } -} - -impl UnixStream { - pub async fn connect(path: std::path::PathBuf) -> std::io::Result { - let join_handle = tokio::task::spawn_blocking(|| -> std::io::Result<_> { - // TODO: For Windows, manually open a SOCKET ala mio-uds-windows and use that instead. - let inner = std::os::unix::net::UnixStream::connect(path)?; - Ok(inner) - }); - let inner = join_handle.await??; - let inner = std::sync::Arc::new(inner); - - - let pending_read = std::sync::Arc::new(std::sync::Mutex::new(PendingRead { - bytes: bytes::BytesMut::new(), - err: None, - eof: false, - })); - - let (reader_sender, reader_receiver) = std::sync::mpsc::channel::(); - let _ = std::thread::spawn({ - let inner = inner.clone(); - let pending_read = pending_read.clone(); - - move || -> std::io::Result<()> { - let mut buf = vec![0_u8; 1024]; - - while let Ok(waker) = reader_receiver.recv() { - match pending_read.lock() { - Ok(pending_read) => { - let pending_read = &*pending_read; - - if !pending_read.bytes.is_empty() || pending_read.eof || pending_read.err.is_some() { - waker.wake(); - continue; - } - }, - - Err(_) => break, - } - - let read = std::io::Read::read(&mut &*inner, &mut buf); - - match pending_read.lock() { - Ok(mut pending_read) => { - let pending_read = &mut *pending_read; - - match read { - Ok(0) => pending_read.eof = true, - Ok(read) => pending_read.bytes.extend_from_slice(&buf[..read]), - Err(err) => { - pending_read.err = Some(err); - pending_read.eof = true; - }, - } - - waker.wake(); - }, - - Err(_) => break, - } - } - - Ok(()) - } - }); - - - let pending_write = std::sync::Arc::new(std::sync::Mutex::new(PendingWrite { - bytes: bytes::BytesMut::new(), - flushed: true, - err: None, - eof: false, - })); - - let (flush_sender, flush_receiver) = std::sync::mpsc::channel::(); - let _ = std::thread::spawn({ - let pending_write = pending_write.clone(); - - move || -> std::io::Result<()> { - 'outer: while let Ok(waker) = flush_receiver.recv() { - loop { - let pending_write_bytes = match pending_write.lock() { - Ok(mut pending_write) => { - let pending_write = &mut *pending_write; - - if !pending_write.bytes.is_empty() && !pending_write.eof && pending_write.err.is_none() { - Some(pending_write.bytes.split()) - } - else { - None - } - }, - - Err(_) => break 'outer, - }; - - let result = pending_write_bytes.map_or(Ok(()), |pending_write_bytes| std::io::Write::write_all(&mut &*inner, &*pending_write_bytes) - .and_then(|_| std::io::Write::flush(&mut &*inner))); - - match pending_write.lock() { - Ok(mut pending_write) => { - let pending_write = &mut *pending_write; - - match result { - Ok(()) => - if pending_write.bytes.is_empty() { - pending_write.flushed = true - } - else { - // More pending writes happened while we were flushing - continue; - }, - - Err(err) => { - pending_write.err = Some(err); - pending_write.eof = true; - }, - } - }, - - Err(_) => break 'outer, - } - - waker.wake(); - break; - } - } - - Ok(()) - } - }); - - - Ok(UnixStream { - pending_read, - reader_sender, - - pending_write, - flush_sender, - }) - } -} - -impl hyper::client::connect::Connection for UnixStream { - fn connected(&self) -> hyper::client::connect::Connected { - hyper::client::connect::Connected::new() - } -} - -impl tokio::io::AsyncRead for UnixStream { - fn poll_read(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, buf: &mut [u8]) -> std::task::Poll> { - match self.pending_read.lock() { - Ok(mut pending_read) => { - let pending_read = &mut *pending_read; - - if !pending_read.bytes.is_empty() { - let len = std::cmp::min(pending_read.bytes.len(), buf.len()); - buf[..len].copy_from_slice(&pending_read.bytes[..len]); - bytes::Buf::advance(&mut pending_read.bytes, len); - std::task::Poll::Ready(Ok(len)) - } - else if let Some(err) = pending_read.err.take() { - std::task::Poll::Ready(Err(err)) - } - else if pending_read.eof { - std::task::Poll::Ready(Ok(0)) - } - else { - match self.reader_sender.send(cx.waker().clone()) { - Ok(()) => std::task::Poll::Pending, - Err(_) => std::task::Poll::Ready(Err(std::io::Error::new(std::io::ErrorKind::Other, "reader receiver dropped"))), - } - } - }, - - Err(_) => std::task::Poll::Ready(Err(std::io::Error::new(std::io::ErrorKind::Other, "reader mutex poisoned"))), - } - } -} - -impl tokio::io::AsyncWrite for UnixStream { - fn poll_write(self: std::pin::Pin<&mut Self>, _cx: &mut std::task::Context<'_>, buf: &[u8]) -> std::task::Poll> { - match self.pending_write.lock() { - Ok(mut pending_write) => { - let pending_write = &mut *pending_write; - - if let Some(err) = pending_write.err.take() { - std::task::Poll::Ready(Err(err)) - } - else if pending_write.eof { - std::task::Poll::Ready(Ok(0)) - } - else { - pending_write.bytes.extend_from_slice(buf); - pending_write.flushed = false; - std::task::Poll::Ready(Ok(buf.len())) - } - }, - - Err(_) => std::task::Poll::Ready(Err(std::io::Error::new(std::io::ErrorKind::Other, "writer mutex poisoned"))), - } - } - - fn poll_flush(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll> { - match self.pending_write.lock() { - Ok(mut pending_write) => { - let pending_write = &mut *pending_write; - - if let Some(err) = pending_write.err.take() { - std::task::Poll::Ready(Err(err)) - } - else if pending_write.flushed { - std::task::Poll::Ready(Ok(())) - } - else { - match self.flush_sender.send(cx.waker().clone()) { - Ok(()) => std::task::Poll::Pending, - Err(_) => std::task::Poll::Ready(Err(std::io::Error::new(std::io::ErrorKind::Other, "flush receiver dropped"))), - } - } - }, - - Err(_) => std::task::Poll::Ready(Err(std::io::Error::new(std::io::ErrorKind::Other, "writer mutex poisoned"))), - } - } - - fn poll_shutdown(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll> { - self.poll_flush(cx) - } -} - -#[derive(Clone, Debug, Default)] -pub struct UdsConnector; - -impl UdsConnector { - pub fn new() -> Self { - Default::default() - } -} - -impl hyper::service::Service for UdsConnector { - type Response = UnixStream; - type Error = std::io::Error; - type Future = std::pin::Pin> + Send>>; - - fn poll_ready(&mut self, _cx: &mut std::task::Context<'_>) -> std::task::Poll> { - std::task::Poll::Ready(Ok(())) - } - - fn call(&mut self, req: hyper::Uri) -> Self::Future { - Box::pin(async move { - let scheme = req.scheme_str(); - if scheme != Some("unix") { - return Err(std::io::Error::new(std::io::ErrorKind::Other, format!(r#"expected req to have "unix" scheme but it has {:?}"#, scheme))); - } - - let host = req.host(); - - let path: std::path::PathBuf = - host - .ok_or_else(|| format!("could not decode UDS path from req host {:?}", host)) - .and_then(|host| hex::decode(host).map_err(|err| format!("could not decode UDS path from req host {:?}: {}", host, err))) - .and_then(|path| String::from_utf8(path).map_err(|err| format!("could not decode UDS path from req host {:?}: {}", host, err))) - .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))? - .into(); - - let stream = UnixStream::connect(path).await?; - Ok(stream) - }) - } -} - -pub fn make_hyper_uri(base: &str, path: &str) -> Result::Err> { - let host = hex::encode(base.as_bytes()); - let uri = format!("unix://{}:0{}", host, path); - let uri = uri.parse()?; - Ok(uri) -} - -// TODO -// impl hyper::Accept for UnixAccept { -// fn accept() { -// UnixStream { -// } -// } -// } diff --git a/edge-modules/api-proxy-module/src/lib.rs b/edge-modules/api-proxy-module/src/lib.rs index d3019c2b2d3..09a4aa4478a 100644 --- a/edge-modules/api-proxy-module/src/lib.rs +++ b/edge-modules/api-proxy-module/src/lib.rs @@ -11,4 +11,4 @@ )] pub mod monitors; -pub mod signals; +pub mod shutdown; diff --git a/edge-modules/api-proxy-module/src/main.rs b/edge-modules/api-proxy-module/src/main.rs index 40d1a18874d..ebea339e796 100644 --- a/edge-modules/api-proxy-module/src/main.rs +++ b/edge-modules/api-proxy-module/src/main.rs @@ -7,12 +7,13 @@ clippy::use_self, clippy::match_same_arms, clippy::must_use_candidate, - clippy::missing_errors_doc + clippy::missing_errors_doc, + clippy::missing_panics_doc )] use std::{process::Stdio, sync::Arc}; use anyhow::{Context, Error, Result}; -use futures::select; +use futures_util::select; use log::{error, info, warn, LevelFilter}; use tokio::{ process::{Child, Command}, @@ -22,7 +23,7 @@ use tokio::{ use api_proxy_module::{ monitors::{certs_monitor, config_monitor, shutdown_handle}, - signals::shutdown, + shutdown, }; use shutdown_handle::ShutdownHandle; @@ -121,12 +122,13 @@ pub fn nginx_controller_start( //Start nginx loop { - let nginx_start = - nginx_command(proxy_name, program_path, &start_proxy_args, "start")?.fuse(); - futures::pin_mut!(nginx_start); + let nginx_start = nginx_command(proxy_name, program_path, &start_proxy_args, "start")?; + futures_util::pin_mut!(nginx_start); info!("Starting/Restarting API-Proxy"); loop { + let nginx_start = nginx_start.wait().fuse(); + //Shutdown nginx on ctrl_c or signal let wait_shutdown_ctrl_c = shutdown::shutdown().fuse(); let wait_shutdown_signal = shutdown_signal.notified().fuse(); @@ -135,7 +137,8 @@ pub fn nginx_controller_start( let cert_reload = notify_server_cert_reload_api_proxy.notified().fuse(); let config_reload = notify_config_reload_api_proxy.notified().fuse(); - futures::pin_mut!( + futures_util::pin_mut!( + nginx_start, wait_shutdown_ctrl_c, wait_shutdown_signal, cert_reload, @@ -157,6 +160,7 @@ pub fn nginx_controller_start( // Stop nginx and restart nginx info!("Request to restart Nginx received"); nginx_command(proxy_name, program_path, &stop_proxy_args, "stop")? + .wait() .await .context("Error running the command")?; @@ -167,6 +171,7 @@ pub fn nginx_controller_start( // Reload nginx config info!("Request to reload Nginx received"); nginx_command(proxy_name, program_path, &reload_proxy_args, "reload")? + .wait() .await .context("Error running the command")?; } diff --git a/edge-modules/api-proxy-module/src/monitors/certs_monitor.rs b/edge-modules/api-proxy-module/src/monitors/certs_monitor.rs index 4141d8cce98..46a354c6c36 100644 --- a/edge-modules/api-proxy-module/src/monitors/certs_monitor.rs +++ b/edge-modules/api-proxy-module/src/monitors/certs_monitor.rs @@ -2,7 +2,10 @@ use std::{env, sync::Arc}; use anyhow::{Context, Error, Result}; use chrono::{DateTime, Duration, Utc}; -use futures_util::future::Either; +use futures_util::{ + future::{self, Either}, + pin_mut, +}; use log::{error, info, warn}; use tokio::{sync::Notify, task::JoinHandle, time}; @@ -51,12 +54,9 @@ pub fn start( //Loop until trust bundle is received. while !new_trust_bundle { let wait_shutdown = shutdown_signal.notified(); - futures::pin_mut!(wait_shutdown); - - if let Either::Right(_) = - futures::future::select(time::delay_for(CERTIFICATE_POLL_INTERVAL), wait_shutdown) - .await - { + let timeout = time::sleep(CERTIFICATE_POLL_INTERVAL); + pin_mut!(wait_shutdown, timeout); + if let Either::Right(_) = future::select(timeout, wait_shutdown).await { warn!("Shutting down certs monitor!"); return Ok(()); } @@ -80,7 +80,7 @@ pub fn start( } //Trust bundle just received. Request for a reset of the API proxy. - notify_trust_bundle_reload_api_proxy.notify(); + notify_trust_bundle_reload_api_proxy.notify_one(); info!("Starting certs monitoring loop"); @@ -89,12 +89,10 @@ pub fn start( //If the system clock gets readjusted while the task is sleeping, the system might wake up after the certificate expiry. loop { let wait_shutdown = shutdown_signal.notified(); - futures::pin_mut!(wait_shutdown); + let timeout = time::sleep(CERTIFICATE_POLL_INTERVAL); + pin_mut!(wait_shutdown, timeout); - if let Either::Right(_) = - futures::future::select(time::delay_for(CERTIFICATE_POLL_INTERVAL), wait_shutdown) - .await - { + if let Either::Right(_) = future::select(timeout, wait_shutdown).await { warn!("Shutting down certs monitor!"); return Ok(()); } @@ -121,7 +119,7 @@ pub fn start( }; if new_server_cert { - notify_server_cert_reload_api_proxy.notify(); + notify_server_cert_reload_api_proxy.notify_one(); } } }); diff --git a/edge-modules/api-proxy-module/src/monitors/config_monitor.rs b/edge-modules/api-proxy-module/src/monitors/config_monitor.rs index 8121b24265d..bf098967948 100644 --- a/edge-modules/api-proxy-module/src/monitors/config_monitor.rs +++ b/edge-modules/api-proxy-module/src/monitors/config_monitor.rs @@ -2,19 +2,15 @@ use std::{sync::Arc, time::Duration}; use anyhow::{Context, Error, Result}; use chrono::Utc; -use futures_util::future::Either; +use futures_util::{future::Either, pin_mut, StreamExt}; use log::{error, info, warn}; use tokio::{sync::Notify, task::JoinHandle}; -use super::config_parser; -use super::file; -use super::shutdown_handle; -use super::token_manager; - use azure_iot_mqtt::{module::Client, Transport::Tcp, TwinProperties}; -use config_parser::ConfigParser; -use shutdown_handle::ShutdownHandle; -use token_manager::TokenManager; + +use crate::monitors::{ + config_parser::ConfigParser, file, shutdown_handle::ShutdownHandle, token_manager::TokenManager, +}; const PROXY_CONFIG_TAG: &str = "proxy_config"; const PROXY_CONFIG_PATH_RAW: &str = "/app/nginx_default_config.conf"; @@ -46,7 +42,6 @@ pub fn start( mut client: Client, notify_received_config: Arc, ) -> Result<(JoinHandle>, ShutdownHandle), Error> { - use futures_util::StreamExt; let shutdown_signal = Arc::new(Notify::new()); let shutdown_handle = ShutdownHandle(shutdown_signal.clone()); @@ -59,17 +54,17 @@ pub fn start( let monitor_loop: JoinHandle> = tokio::spawn(async move { loop { let wait_shutdown = shutdown_signal.notified(); - futures::pin_mut!(wait_shutdown); + pin_mut!(wait_shutdown); let get_new_sas_token = token_manager.poll_new_sas_token( Utc::now(), chrono::Duration::seconds(TOKEN_VALIDITY_SECONDS), chrono::Duration::seconds(TOKEN_EXPIRY_SECONDS_MARGIN), ); - futures::pin_mut!(get_new_sas_token); - let reload_config = futures::future::select(get_new_sas_token, client.next()); + pin_mut!(get_new_sas_token); + let reload_config = futures_util::future::select(get_new_sas_token, client.next()); let parse_config_request = - match futures::future::select(wait_shutdown, reload_config).await { + match futures_util::future::select(wait_shutdown, reload_config).await { Either::Left(_) => { warn!("Shutting down config monitor!"); return Ok(()); @@ -115,7 +110,7 @@ pub fn start( if parse_config_request { match parse_config(&config_parser) { //Notify watchdog config is there - Ok(()) => notify_received_config.notify(), + Ok(()) => notify_received_config.notify_one(), Err(error) => error!("Error while parsing default config: {}", error), }; }; diff --git a/edge-modules/api-proxy-module/src/monitors/shutdown_handle.rs b/edge-modules/api-proxy-module/src/monitors/shutdown_handle.rs index 383f06d1515..4cf8bde8a3b 100644 --- a/edge-modules/api-proxy-module/src/monitors/shutdown_handle.rs +++ b/edge-modules/api-proxy-module/src/monitors/shutdown_handle.rs @@ -7,6 +7,6 @@ pub struct ShutdownHandle(pub Arc); impl ShutdownHandle { pub async fn shutdown(self) { - self.0.notify(); + self.0.notify_one(); } } diff --git a/edge-modules/api-proxy-module/src/monitors/token_client.rs b/edge-modules/api-proxy-module/src/monitors/token_client.rs index c015d249c44..e93f9e76a9f 100644 --- a/edge-modules/api-proxy-module/src/monitors/token_client.rs +++ b/edge-modules/api-proxy-module/src/monitors/token_client.rs @@ -1,11 +1,9 @@ use anyhow::{Error, Result}; use log::info; -use percent_encoding::{define_encode_set, percent_encode, PATH_SEGMENT_ENCODE_SET}; +use percent_encoding::percent_encode; use url::form_urlencoded::Serializer as UrlSerializer; -define_encode_set! { - pub IOTHUB_ENCODE_SET = [PATH_SEGMENT_ENCODE_SET] | { '=' } -} +use edgelet_client::IOTHUB_ENCODE_SET; pub struct TokenClient { device_id: String, diff --git a/edge-modules/api-proxy-module/src/monitors/token_manager.rs b/edge-modules/api-proxy-module/src/monitors/token_manager.rs index aa8f7914a02..2e2397c2e80 100644 --- a/edge-modules/api-proxy-module/src/monitors/token_manager.rs +++ b/edge-modules/api-proxy-module/src/monitors/token_manager.rs @@ -65,7 +65,7 @@ impl TokenManager { .unwrap_or(0); let delay_seconds = tokio::time::Duration::from_secs(delay_seconds); - time::delay_for(delay_seconds).await; + time::sleep(delay_seconds).await; } else { error!("Token has expired before API proxy was able to request a new one. Requesting a new SAS token now"); } diff --git a/edge-modules/api-proxy-module/src/shutdown.rs b/edge-modules/api-proxy-module/src/shutdown.rs new file mode 100644 index 00000000000..15149a73560 --- /dev/null +++ b/edge-modules/api-proxy-module/src/shutdown.rs @@ -0,0 +1,21 @@ +use futures_util::{ + future::{self, Either}, + pin_mut, +}; +use log::info; +use tokio::signal::unix::{signal, SignalKind}; + +pub async fn shutdown() { + let mut terminate = signal(SignalKind::terminate()).expect("signal handling failed"); + let terminate = terminate.recv(); + + let mut interrupt = signal(SignalKind::interrupt()).expect("signal handling failed"); + let interrupt = interrupt.recv(); + + pin_mut!(terminate, interrupt); + + match future::select(terminate, interrupt).await { + Either::Left(_) => info!("SIGTERM received"), + Either::Right(_) => info!("SIGINT received"), + } +} diff --git a/edge-modules/api-proxy-module/src/signals/mod.rs b/edge-modules/api-proxy-module/src/signals/mod.rs deleted file mode 100644 index de8498c7caf..00000000000 --- a/edge-modules/api-proxy-module/src/signals/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod shutdown; diff --git a/edge-modules/api-proxy-module/src/signals/shutdown.rs b/edge-modules/api-proxy-module/src/signals/shutdown.rs deleted file mode 100644 index 1378c0a1e59..00000000000 --- a/edge-modules/api-proxy-module/src/signals/shutdown.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -// Adapted from the conduit proxy signal handling: -// https://github.com/runconduit/conduit/blob/master/proxy/src/signal.rs - -use futures_util::future::{self, Either}; -use futures_util::stream::StreamExt; -use log::info; -use tokio::signal::unix::{signal, SignalKind}; - -pub async fn shutdown() { - let mut term = signal(SignalKind::terminate()).expect("signal handling failed"); - let mut interrupt = signal(SignalKind::interrupt()).expect("signal handling failed"); - match future::select(term.next(), interrupt.next()).await { - Either::Left(_) => info!("SIGTERM received"), - Either::Right(_) => info!("SIGINT received"), - } -}