From 6b625757492a9d0426d2afdcc94b9ae4f059201c Mon Sep 17 00:00:00 2001 From: henil Date: Fri, 9 Dec 2022 18:35:51 +0530 Subject: [PATCH 1/2] refactor: Remove old rumqttd-old in favour of rumqttd --- Cargo.lock | 401 +------- Cargo.toml | 1 - benchmarks/Cargo.toml | 2 +- benchmarks/clients/common.rs | 50 - rumqttd-old/Cargo.toml | 46 - rumqttd-old/README.md | 56 -- rumqttd-old/config/rumqttd.conf | 45 - rumqttd-old/config/rumqttd0.conf | 69 -- rumqttd-old/config/rumqttd1.conf | 67 -- rumqttd-old/config/rumqttd2.conf | 67 -- rumqttd-old/docker/.gitignore | 3 - rumqttd-old/docker/Dockerfile | 7 - rumqttd-old/docker/README.md | 11 - rumqttd-old/docker/build.sh | 17 - rumqttd-old/docker/stage/README.md | 2 - rumqttd-old/examples/async_broker.rs | 55 -- rumqttd-old/examples/broker.rs | 43 - rumqttd-old/rumqttd.test | Bin 30709 -> 0 bytes rumqttd-old/src/async_locallink.rs | 199 ---- rumqttd-old/src/consolelink.rs | 79 -- rumqttd-old/src/lib.rs | 536 ----------- rumqttd-old/src/locallink.rs | 153 ---- rumqttd-old/src/main.rs | 61 -- rumqttd-old/src/mqttbytes/mod.rs | 335 ------- rumqttd-old/src/mqttbytes/topic.rs | 125 --- rumqttd-old/src/mqttbytes/v4/connack.rs | 124 --- rumqttd-old/src/mqttbytes/v4/connect.rs | 409 --------- rumqttd-old/src/mqttbytes/v4/mod.rs | 85 -- rumqttd-old/src/mqttbytes/v4/ping.rs | 11 - rumqttd-old/src/mqttbytes/v4/puback.rs | 74 -- rumqttd-old/src/mqttbytes/v4/pubcomp.rs | 45 - rumqttd-old/src/mqttbytes/v4/publish.rs | 268 ------ rumqttd-old/src/mqttbytes/v4/pubrec.rs | 44 - rumqttd-old/src/mqttbytes/v4/pubrel.rs | 44 - rumqttd-old/src/mqttbytes/v4/suback.rs | 112 --- rumqttd-old/src/mqttbytes/v4/subscribe.rs | 235 ----- rumqttd-old/src/mqttbytes/v4/unsuback.rs | 34 - rumqttd-old/src/mqttbytes/v4/unsubscribe.rs | 49 - rumqttd-old/src/network.rs | 195 ---- rumqttd-old/src/remotelink.rs | 288 ------ rumqttd-old/src/rumqttlog/logs/acks.rs | 67 -- rumqttd-old/src/rumqttlog/logs/connections.rs | 68 -- rumqttd-old/src/rumqttlog/logs/data.rs | 133 --- rumqttd-old/src/rumqttlog/logs/mod.rs | 91 -- rumqttd-old/src/rumqttlog/logs/topics.rs | 46 - rumqttd-old/src/rumqttlog/mod.rs | 40 - .../src/rumqttlog/router/connection.rs | 130 --- rumqttd-old/src/rumqttlog/router/metrics.rs | 51 -- rumqttd-old/src/rumqttlog/router/mod.rs | 276 ------ .../src/rumqttlog/router/readyqueue.rs | 35 - rumqttd-old/src/rumqttlog/router/router.rs | 861 ------------------ rumqttd-old/src/rumqttlog/router/slab.rs | 80 -- rumqttd-old/src/rumqttlog/router/tracker.rs | 289 ------ rumqttd-old/src/rumqttlog/waiters/mod.rs | 111 --- rumqttd-old/src/state.rs | 372 -------- rumqttd/config.patch | 83 -- 56 files changed, 32 insertions(+), 7148 deletions(-) delete mode 100644 benchmarks/clients/common.rs delete mode 100644 rumqttd-old/Cargo.toml delete mode 100644 rumqttd-old/README.md delete mode 100644 rumqttd-old/config/rumqttd.conf delete mode 100644 rumqttd-old/config/rumqttd0.conf delete mode 100644 rumqttd-old/config/rumqttd1.conf delete mode 100644 rumqttd-old/config/rumqttd2.conf delete mode 100644 rumqttd-old/docker/.gitignore delete mode 100644 rumqttd-old/docker/Dockerfile delete mode 100644 rumqttd-old/docker/README.md delete mode 100755 rumqttd-old/docker/build.sh delete mode 100644 rumqttd-old/docker/stage/README.md delete mode 100644 rumqttd-old/examples/async_broker.rs delete mode 100644 rumqttd-old/examples/broker.rs delete mode 100644 rumqttd-old/rumqttd.test delete mode 100644 rumqttd-old/src/async_locallink.rs delete mode 100644 rumqttd-old/src/consolelink.rs delete mode 100644 rumqttd-old/src/lib.rs delete mode 100644 rumqttd-old/src/locallink.rs delete mode 100644 rumqttd-old/src/main.rs delete mode 100644 rumqttd-old/src/mqttbytes/mod.rs delete mode 100644 rumqttd-old/src/mqttbytes/topic.rs delete mode 100644 rumqttd-old/src/mqttbytes/v4/connack.rs delete mode 100644 rumqttd-old/src/mqttbytes/v4/connect.rs delete mode 100644 rumqttd-old/src/mqttbytes/v4/mod.rs delete mode 100644 rumqttd-old/src/mqttbytes/v4/ping.rs delete mode 100644 rumqttd-old/src/mqttbytes/v4/puback.rs delete mode 100644 rumqttd-old/src/mqttbytes/v4/pubcomp.rs delete mode 100644 rumqttd-old/src/mqttbytes/v4/publish.rs delete mode 100644 rumqttd-old/src/mqttbytes/v4/pubrec.rs delete mode 100644 rumqttd-old/src/mqttbytes/v4/pubrel.rs delete mode 100644 rumqttd-old/src/mqttbytes/v4/suback.rs delete mode 100644 rumqttd-old/src/mqttbytes/v4/subscribe.rs delete mode 100644 rumqttd-old/src/mqttbytes/v4/unsuback.rs delete mode 100644 rumqttd-old/src/mqttbytes/v4/unsubscribe.rs delete mode 100644 rumqttd-old/src/network.rs delete mode 100644 rumqttd-old/src/remotelink.rs delete mode 100644 rumqttd-old/src/rumqttlog/logs/acks.rs delete mode 100644 rumqttd-old/src/rumqttlog/logs/connections.rs delete mode 100644 rumqttd-old/src/rumqttlog/logs/data.rs delete mode 100644 rumqttd-old/src/rumqttlog/logs/mod.rs delete mode 100644 rumqttd-old/src/rumqttlog/logs/topics.rs delete mode 100644 rumqttd-old/src/rumqttlog/mod.rs delete mode 100644 rumqttd-old/src/rumqttlog/router/connection.rs delete mode 100644 rumqttd-old/src/rumqttlog/router/metrics.rs delete mode 100644 rumqttd-old/src/rumqttlog/router/mod.rs delete mode 100644 rumqttd-old/src/rumqttlog/router/readyqueue.rs delete mode 100644 rumqttd-old/src/rumqttlog/router/router.rs delete mode 100644 rumqttd-old/src/rumqttlog/router/slab.rs delete mode 100644 rumqttd-old/src/rumqttlog/router/tracker.rs delete mode 100644 rumqttd-old/src/rumqttlog/waiters/mod.rs delete mode 100644 rumqttd-old/src/state.rs delete mode 100644 rumqttd/config.patch diff --git a/Cargo.lock b/Cargo.lock index 9fc7eb6d6..c75ac9cc9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,15 +67,6 @@ dependencies = [ "libc", ] -[[package]] -name = "ansi_term" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -dependencies = [ - "winapi", -] - [[package]] name = "ansi_term" version = "0.12.1" @@ -91,35 +82,6 @@ version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" -[[package]] -name = "argh" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c375edecfd2074d5edcc31396860b6e54b6f928714d0e097b983053fac0cabe3" -dependencies = [ - "argh_derive", - "argh_shared", -] - -[[package]] -name = "argh_derive" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa013479b80109a1bf01a039412b0f0013d716f36921226d86c6709032fb7a03" -dependencies = [ - "argh_shared", - "heck 0.4.0", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "argh_shared" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "149f75bbec1827618262e0855a68f0f9a7f2edc13faebf33c4f16d6725edb6a9" - [[package]] name = "arrayvec" version = "0.4.12" @@ -205,7 +167,7 @@ checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" dependencies = [ "addr2line", "cc", - "cfg-if 1.0.0", + "cfg-if", "libc", "miniz_oxide", "object", @@ -229,7 +191,7 @@ dependencies = [ "pprof", "pretty_env_logger", "rumqttc", - "rumqttd-old", + "rumqttd", "serde", "serde_json", "tokio", @@ -329,12 +291,6 @@ dependencies = [ "serde", ] -[[package]] -name = "cache-padded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" - [[package]] name = "cc" version = "1.0.73" @@ -344,12 +300,6 @@ dependencies = [ "jobserver", ] -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.0" @@ -380,7 +330,7 @@ version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ - "ansi_term 0.12.1", + "ansi_term", "atty", "bitflags", "strsim", @@ -409,15 +359,6 @@ dependencies = [ "termcolor", ] -[[package]] -name = "concurrent-queue" -version = "1.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c" -dependencies = [ - "cache-padded", -] - [[package]] name = "config" version = "0.13.2" @@ -437,17 +378,6 @@ dependencies = [ "yaml-rust", ] -[[package]] -name = "confy" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2913470204e9e8498a0f31f17f90a0de801ae92c8c5ac18c49af4819e6786697" -dependencies = [ - "directories", - "serde", - "toml", -] - [[package]] name = "core-foundation" version = "0.9.3" @@ -470,7 +400,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -488,7 +418,7 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -498,7 +428,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" dependencies = [ "autocfg", - "cfg-if 1.0.0", + "cfg-if", "crossbeam-utils", "memoffset", "scopeguard", @@ -510,7 +440,7 @@ version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -589,12 +519,6 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" -[[package]] -name = "difference" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" - [[package]] name = "digest" version = "0.9.0" @@ -614,27 +538,6 @@ dependencies = [ "crypto-common", ] -[[package]] -name = "directories" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "551a778172a450d7fc12e629ca3b0428d00f6afa9a43da1b630d54604e97371c" -dependencies = [ - "cfg-if 0.1.10", - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - [[package]] name = "dlv-list" version = "0.3.0" @@ -680,12 +583,6 @@ dependencies = [ "termcolor", ] -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - [[package]] name = "fastrand" version = "1.8.0" @@ -701,7 +598,7 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "redox_syscall", "windows-sys", @@ -892,7 +789,7 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", @@ -939,25 +836,6 @@ dependencies = [ "crc32fast", ] -[[package]] -name = "h2" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "hashbrown" version = "0.12.3" @@ -967,31 +845,6 @@ dependencies = [ "ahash", ] -[[package]] -name = "headers" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" -dependencies = [ - "base64", - "bitflags", - "bytes", - "headers-core", - "http", - "httpdate", - "mime", - "sha1 0.10.5", -] - -[[package]] -name = "headers-core" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" -dependencies = [ - "http", -] - [[package]] name = "heck" version = "0.3.3" @@ -1069,7 +922,6 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", "http", "http-body", "httparse", @@ -1140,7 +992,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -1170,17 +1022,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" -[[package]] -name = "jackiechan" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f111fa9ca959198fdac9765795602f095c8160423daf7801cb9be323158b80f0" -dependencies = [ - "concurrent-queue", - "event-listener", - "futures-core", -] - [[package]] name = "jemalloc-sys" version = "0.3.2" @@ -1245,7 +1086,7 @@ checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" dependencies = [ "arrayvec 0.5.2", "bitflags", - "cfg-if 1.0.0", + "cfg-if", "ryu", "static_assertions", ] @@ -1302,7 +1143,7 @@ version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -1335,16 +1176,6 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" -[[package]] -name = "memmap" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "memmap2" version = "0.5.7" @@ -1522,7 +1353,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" dependencies = [ "bitflags", - "cfg-if 1.0.0", + "cfg-if", "libc", ] @@ -1670,7 +1501,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" dependencies = [ "bitflags", - "cfg-if 1.0.0", + "cfg-if", "foreign-types", "libc", "once_cell", @@ -1760,7 +1591,7 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "instant", "libc", "redox_syscall", @@ -1774,7 +1605,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "redox_syscall", "smallvec", @@ -1914,7 +1745,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6472bfed9475542ac46c518734a8d06d71b0f6cb2c17f904aa301711a57786f" dependencies = [ "backtrace", - "cfg-if 1.0.0", + "cfg-if", "findshlibs", "inferno", "libc", @@ -1937,18 +1768,6 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" -[[package]] -name = "pretty_assertions" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427" -dependencies = [ - "ansi_term 0.11.0", - "ctor", - "difference", - "output_vt100", -] - [[package]] name = "pretty_assertions" version = "1.3.0" @@ -2021,7 +1840,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ae5a4388762d5815a9fc0dea33c56b021cdc8dde0c55e0c9ca57197254b0cab" dependencies = [ "bytes", - "cfg-if 1.0.0", + "cfg-if", "cmake", "heck 0.4.0", "itertools", @@ -2153,17 +1972,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "redox_users" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" -dependencies = [ - "getrandom", - "redox_syscall", - "thiserror", -] - [[package]] name = "regex" version = "1.6.0" @@ -2273,7 +2081,7 @@ dependencies = [ "matches", "native-tls", "pollster", - "pretty_assertions 1.3.0", + "pretty_assertions", "pretty_env_logger", "rustls", "rustls-native-certs", @@ -2299,7 +2107,7 @@ dependencies = [ "metrics", "metrics-exporter-prometheus", "parking_lot 0.11.2", - "pretty_assertions 1.3.0", + "pretty_assertions", "pretty_env_logger", "rouille", "rustls-pemfile 0.3.0", @@ -2311,7 +2119,7 @@ dependencies = [ "tokio", "tokio-native-tls", "tokio-rustls", - "tokio-tungstenite 0.15.0", + "tokio-tungstenite", "tokio-util", "tracing", "tracing-subscriber", @@ -2320,37 +2128,13 @@ dependencies = [ "x509-parser", ] -[[package]] -name = "rumqttd-old" -version = "0.11.0" -dependencies = [ - "argh", - "bytes", - "confy", - "futures-util", - "jackiechan", - "jemallocator", - "log", - "pprof", - "pretty_assertions 0.6.1", - "pretty_env_logger", - "rustls-pemfile 0.3.0", - "segments", - "serde", - "thiserror", - "tokio", - "tokio-native-tls", - "tokio-rustls", - "warp", -] - [[package]] name = "rust-ini" version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "ordered-multimap", ] @@ -2402,15 +2186,6 @@ dependencies = [ "security-framework", ] -[[package]] -name = "rustls-pemfile" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" -dependencies = [ - "base64", -] - [[package]] name = "rustls-pemfile" version = "0.3.0" @@ -2457,12 +2232,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "scoped-tls" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" - [[package]] name = "scopeguard" version = "1.1.0" @@ -2502,18 +2271,6 @@ dependencies = [ "libc", ] -[[package]] -name = "segments" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b89c31f9c863dfa573bf2b28f1dc0afc3cdf6f8dfc828f422ba15a0dee18a6" -dependencies = [ - "byteorder", - "fnv", - "log", - "memmap", -] - [[package]] name = "semver" version = "1.0.14" @@ -2551,18 +2308,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa 1.0.3", - "ryu", - "serde", -] - [[package]] name = "sha-1" version = "0.9.8" @@ -2570,23 +2315,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" dependencies = [ "block-buffer 0.9.0", - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "digest 0.9.0", "opaque-debug", ] -[[package]] -name = "sha-1" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" -dependencies = [ - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.10.5", -] - [[package]] name = "sha1" version = "0.6.1" @@ -2602,7 +2336,7 @@ version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "digest 0.10.5", ] @@ -2776,7 +2510,7 @@ version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7890fff842b8db56f2033ebee8f6efe1921475c3830c115995552914fb967580" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "core-foundation-sys", "libc", "ntapi", @@ -2796,7 +2530,7 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "fastrand", "libc", "redox_syscall", @@ -2951,17 +2685,6 @@ dependencies = [ "webpki", ] -[[package]] -name = "tokio-stream" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6edf2d6bc038a43d31353570e27270603f4648d18f5ed10c0e179abe43255af" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - [[package]] name = "tokio-tungstenite" version = "0.15.0" @@ -2975,18 +2698,6 @@ dependencies = [ "tungstenite 0.14.0", ] -[[package]] -name = "tokio-tungstenite" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite 0.17.3", -] - [[package]] name = "tokio-util" version = "0.7.4" @@ -3022,7 +2733,7 @@ version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "log", "pin-project-lite", "tracing-attributes", @@ -3098,7 +2809,7 @@ dependencies = [ "httparse", "log", "rand", - "sha-1 0.9.8", + "sha-1", "thiserror", "url", "utf-8", @@ -3118,32 +2829,13 @@ dependencies = [ "log", "rand", "rustls", - "sha-1 0.9.8", + "sha-1", "thiserror", "url", "utf-8", "webpki", ] -[[package]] -name = "tungstenite" -version = "0.17.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" -dependencies = [ - "base64", - "byteorder", - "bytes", - "http", - "httparse", - "log", - "rand", - "sha-1 0.10.0", - "thiserror", - "url", - "utf-8", -] - [[package]] name = "twoway" version = "0.1.8" @@ -3261,7 +2953,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73ba753d713ec3844652ad2cb7eb56bc71e34213a14faddac7852a10ba88f61e" dependencies = [ "anyhow", - "cfg-if 1.0.0", + "cfg-if", "enum-iterator", "getset", "git2", @@ -3288,37 +2980,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "warp" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed7b8be92646fc3d18b06147664ebc5f48d222686cb11a8755e561a735aacc6d" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "headers", - "http", - "hyper", - "log", - "mime", - "mime_guess", - "multipart", - "percent-encoding", - "pin-project", - "rustls-pemfile 0.2.1", - "scoped-tls", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-stream", - "tokio-tungstenite 0.17.2", - "tokio-util", - "tower-service", - "tracing", -] - [[package]] name = "wasi" version = "0.10.2+wasi-snapshot-preview1" @@ -3337,7 +2998,7 @@ version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "wasm-bindgen-macro", ] diff --git a/Cargo.toml b/Cargo.toml index 4bd1d3e11..33819d2ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,6 @@ members = [ "rumqttc", - "rumqttd-old", "rumqttd", "benchmarks", "benchmarks/simplerouter", diff --git a/benchmarks/Cargo.toml b/benchmarks/Cargo.toml index d478a099e..9c6b397ff 100644 --- a/benchmarks/Cargo.toml +++ b/benchmarks/Cargo.toml @@ -12,7 +12,7 @@ itoa = "0.4" pprof = { version = "0.10", features = ["flamegraph", "prost-codec"] } pretty_env_logger = "0.4" rumqttc = { path = "../rumqttc" } -rumqttd-old = { path = "../rumqttd-old" } +rumqttd = { path = "../rumqttd" } serde = { version = "1", features = ["derive"] } serde_json = "1" tokio = { version = "1", features = ["full"] } diff --git a/benchmarks/clients/common.rs b/benchmarks/clients/common.rs deleted file mode 100644 index 46dcd6443..000000000 --- a/benchmarks/clients/common.rs +++ /dev/null @@ -1,50 +0,0 @@ -use librumqttd::rumqttlog::router::ConnectionAck; -use librumqttd::rumqttlog::{Connection, Event, Notification, Receiver, Sender}; -use pprof::{protos::Message, ProfilerGuard}; -use serde::{Deserialize, Serialize}; -use std::fs::File; -use std::io::Write; - -#[allow(unused)] -pub fn profile(name: &str, guard: ProfilerGuard) { - if let Ok(report) = guard.report().build() { - let mut file = File::create(name).unwrap(); - let profile = report.pprof().unwrap(); - - let mut content = Vec::new(); - profile.encode(&mut content).unwrap(); - file.write_all(&content).unwrap(); - }; -} - -#[allow(unused)] -pub async fn new_connection( - id: &str, - cap: usize, - router_tx: &Sender<(usize, Event)>, -) -> (usize, Receiver) { - let (connection, this_rx) = Connection::new_remote(id, true, cap); - - // send a connection request with a dummy id - let message = (0, Event::Connect(connection)); - router_tx.send(message).unwrap(); - - // wait for ack from router - let (id, ..) = match this_rx.recv().unwrap() { - Notification::ConnectionAck(ConnectionAck::Success(id)) => id, - Notification::ConnectionAck(ConnectionAck::Failure(e)) => { - panic!("Connection failed {:?}", e) - } - message => panic!("Not connection ack = {:?}", message), - }; - - (id, this_rx) -} - -#[derive(Serialize, Deserialize)] -pub struct Print { - pub id: String, - pub messages: usize, - pub payload_size: usize, - pub throughput: usize, -} diff --git a/rumqttd-old/Cargo.toml b/rumqttd-old/Cargo.toml deleted file mode 100644 index 88f9b9ccd..000000000 --- a/rumqttd-old/Cargo.toml +++ /dev/null @@ -1,46 +0,0 @@ -[package] -name = "rumqttd-old" -description = "Distributed, embeddable mqtt broker library" -license = "Apache-2.0" -version = "0.11.0" -authors = ["tekjar "] -edition = "2018" -keywords = ["mqtt", "broker", "iot", "kafka", "nats"] -categories = ["network-programming"] -repository = "https://github.com/bytebeamio/rumqtt/" - -[lib] -name = "librumqttd" -path = "src/lib.rs" - -[features] -default = ["use-rustls"] -prof = ["pprof"] -use-rustls = ["tokio-rustls", "rustls-pemfile"] -use-native-tls = ["tokio-native-tls"] - -[dependencies] -argh = "0.1" -bytes = "1" -confy = "0.4" -futures-util = "0.3" -jackiechan = "0.0.4" -log = "0.4" -pretty_env_logger = "0.4" -segments = "0.1" -serde = { version = "1", features = ["derive"] } -thiserror = "1" -tokio = { version = "1", features = ["full"] } -warp = "0.3" - -# Optional -rustls-pemfile = { version = "0.3", optional = true } -tokio-native-tls = { version = "0.3", optional = true } -tokio-rustls = { version = "0.23", optional = true } - -[dev-dependencies] -pretty_assertions = "0.6" - -[target.'cfg(not(target_env = "msvc"))'.dependencies] -jemallocator = "0.3" -pprof = { version = "0.10", features = ["flamegraph", "prost-codec"], optional = true } diff --git a/rumqttd-old/README.md b/rumqttd-old/README.md deleted file mode 100644 index fcd332c9f..000000000 --- a/rumqttd-old/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# rumqttd - -[![crates.io page](https://img.shields.io/crates/v/rumqttd.svg)](https://crates.io/crates/rumqttd) -[![docs.rs page](https://docs.rs/rumqttd/badge.svg)](https://docs.rs/rumqttd) - -## `native-tls` support - -This crate, by default uses the `tokio-rustls` crate. There's also support for the `tokio-native-tls` crate. -Add it to your Cargo.toml like so: - -``` -rumqttd = { version = "0.5", default-features = false, features = ["use-native-tls"] } -``` - -Then in your config file make sure that you use the `pkcs12` entries under `certs` for your cert instead of `cert_path`, `key_path`, etc. - -```toml -[rumqtt.servers.1] -port = 8883 - -[servers.1.cert] -pkcs12_path = "/root/identity.pfx" -pkcs12_pass = "" -``` - -Here's what a Rustls config looks like: - -```toml -[servers.1] -port = 8883 - -[servers.1.cert] -cert_path = "tlsfiles/server.cert.pem" -key_path = "tlsfiles/server.key.pem" -ca_path = "tlsfiles/ca.cert.pem" -``` - - -You can generate the `.p12`/`.pfx` file using `openssl`: - -``` -openssl pkcs12 -export -out identity.pfx -inkey ~/pki/private/test.key -in ~/pki/issued/test.crt -certfile ~/pki/ca.crt -``` - -Make sure if you use a password it matches the entry in `pkcs12_pass`. If no password, use an empty string `""`. - - -### Build and run as Docker image ------------------- - -``` -./build.sh -docker run -it -p 1883:1883 --name rumqttd rumqttd # run foreground -docker run -d -p 1883:1883 --name rumqttd rumqttd # run background -docker run -d -p 1883:1883 -e "RUST_LOG=debug" --name rumqttd rumqttd # override loglevel -``` diff --git a/rumqttd-old/config/rumqttd.conf b/rumqttd-old/config/rumqttd.conf deleted file mode 100644 index ce704a658..000000000 --- a/rumqttd-old/config/rumqttd.conf +++ /dev/null @@ -1,45 +0,0 @@ -# Broker id. Used to identify local node of the replication mesh -id = 0 - -# A commitlog read will pull full segment. Make sure that a segment isn't -# too big as async tcp writes readiness of one connection might affect tail -# latencies of other connection. Not a problem with preempting runtimes -[router] -id = 0 -dir = "/tmp/rumqttd" -max_segment_size = 10240 -max_segment_count = 10 -max_connections = 10001 - -# Configuration of server and connections that it accepts -[servers.1] -listen = "0.0.0.0:1883" -next_connection_delay_ms = 1 - [servers.1.connections] - connection_timeout_ms = 5000 - max_client_id_len = 256 - throttle_delay_ms = 0 - max_payload_size = 5120 - max_inflight_count = 200 - max_inflight_size = 1024 - -# Configuration of server and connections that it accepts -[servers.2] -listen = "0.0.0.0:8883" -next_connection_delay_ms = 10 - # Cert config - [servers.2.cert] - cert_path = "tlsfiles/server.cert.pem" - key_path = "tlsfiles/server.key.pem" - ca_path = "tlsfiles/ca.cert.pem" - # Connection parameters - [servers.2.connections] - connection_timeout_ms = 5000 - max_client_id_len = 256 - throttle_delay_ms = 0 - max_payload_size = 5120 - max_inflight_count = 100 - max_inflight_size = 1024 - -[console] -listen = "0.0.0.0:3030" diff --git a/rumqttd-old/config/rumqttd0.conf b/rumqttd-old/config/rumqttd0.conf deleted file mode 100644 index 633f7191b..000000000 --- a/rumqttd-old/config/rumqttd0.conf +++ /dev/null @@ -1,69 +0,0 @@ -# Broker id. Used to identify local node of the replication mesh -id = 0 - -# A commitlog read will pull full segment. Make sure that a segment isn't -# too big as async tcp writes readiness of one connection might affect tail -# latencies of other connection. Not a problem with preempting runtimes -[router] -id = 0 -dir = "/tmp/rumqttd" -max_segment_size = 10240 -max_segment_count = 10 -max_connections = 10001 - -# Configuration of server and connections that it accepts -[servers.1] -listen = "0.0.0.0:1883" -next_connection_delay_ms = 1 - [servers.1.connections] - connection_timeout_ms = 100 - max_client_id_len = 256 - throttle_delay_ms = 0 - max_payload_size = 2048 - max_inflight_count = 500 - max_inflight_size = 1024 - -# Configuration of server and connections that it accepts -[servers.2] -listen = "0.0.0.0:1883" -next_connection_delay_ms = 10 - # Handling of Certs - [servers.2.cert] - cert_path = "tlsfiles/server.cert.pem" - key_path = "tlsfiles/server.key.pem" - ca_path = "tlsfiles/ca-chain.cert.pem" - # Connection parameters - [servers.2.connections] - connection_timeout_ms = 100 - max_client_id_len = 256 - throttle_delay_ms = 0 - max_payload_size = 2048 - max_inflight_count = 100 - max_inflight_size = 1024 - - -# Cluster configuration. Remote host and port to connect to. -# Mesh is created based on ids. -# 0 connects to 1 & 2 as client -# 1 connects to 2 as client and waits for 0 as a server -# 2 waits for 0 and 1 as a server -[cluster] - [cluster.0] - address = "localhost:1800" - [cluster.1] - address = "localhost:1801" - # [cluster.2] - # address = "localhost:1802" - -# Io configuration for replication -[replicator] -connection_timeout_ms = 100 -max_client_id_len = 256 -throttle_delay_ms = 0 -max_payload_size = 2048 -max_inflight_count = 500 -max_inflight_size = 1024 - -[console] -listen = "0.0.0.0:3030" - diff --git a/rumqttd-old/config/rumqttd1.conf b/rumqttd-old/config/rumqttd1.conf deleted file mode 100644 index 9fd9c1fe0..000000000 --- a/rumqttd-old/config/rumqttd1.conf +++ /dev/null @@ -1,67 +0,0 @@ -# Broker id. Used to identify local node of the replication mesh -id = 1 - -# A commitlog read will pull full segment. Make sure that a segment isn't -# too big as async tcp writes readiness of one connection might affect tail -# latencies of other connection. Not a problem with preempting runtimes -[router] -id = 0 -dir = "/tmp/rumqttd" -max_segment_size = 10240 -max_segment_count = 10 -max_connections = 10001 - -# Configuration of server and connections that it accepts -[servers.1] -listen = "0.0.0.0:1884" -next_connection_delay_ms = 1 - [servers.1.connections] - connection_timeout_ms = 100 - max_client_id_len = 256 - throttle_delay_ms = 0 - max_payload_size = 2048 - max_inflight_count = 500 - max_inflight_size = 1024 - -# Configuration of server and connections that it accepts -[servers.2] -listen = "0.0.0.0:1884" -next_connection_delay_ms = 10 - # Connection parameters - [servers.2.connections] - connection_timeout_ms = 100 - max_client_id_len = 256 - throttle_delay_ms = 0 - max_payload_size = 2048 - max_inflight_count = 100 - max_inflight_size = 1024 - # Certs for connection - [servers.2.certs] - cert_path = "tlsfiles/server.cert.pem" - key_path = "tlsfiles/server.key.pem" - ca_path = "tlsfiles/ca-chain.cert.pem" - -# Cluster configuration. Remote address to connect to. -# Mesh is created based on ids. -# 0 connects to 1 & 2 as client -# 1 connects to 2 as client and waits for 0 as a server -# 2 waits for 0 and 1 as a server -[cluster] - [cluster.0] - address = "localhost:1800" - [cluster.1] - address = "localhost:1801" - # [cluster.2] - # address = "localhost:1802" - -# Io configuration for replication -[replicator] -connection_timeout_ms = 100 -max_client_id_len = 256 -throttle_delay_ms = 0 -max_payload_size = 2048 -max_inflight_count = 500 -max_inflight_size = 1024 - -[console] -listen = "0.0.0.0:3031" diff --git a/rumqttd-old/config/rumqttd2.conf b/rumqttd-old/config/rumqttd2.conf deleted file mode 100644 index 465354ad5..000000000 --- a/rumqttd-old/config/rumqttd2.conf +++ /dev/null @@ -1,67 +0,0 @@ -# Broker id. Used to identify local node of the replication mesh -id = 2 - -# A commitlog read will pull full segment. Make sure that a segment isn't -# too big as async tcp writes readiness of one connection might affect tail -# latencies of other connection. Not a problem with preempting runtimes -[router] -id = 0 -dir = "/tmp/rumqttd" -max_segment_size = 10240 -max_segment_count = 10 -max_connections = 10001 - -# Configuration of server and connections that it accepts -[servers.1] -listen = "0.0.0.0:1885" -next_connection_delay_ms = 1 - [servers.1.connections] - connection_timeout_ms = 100 - max_client_id_len = 256 - throttle_delay_ms = 0 - max_payload_size = 2048 - max_inflight_count = 500 - max_inflight_size = 1024 - -# Configuration of server and connections that it accepts -[servers.2] -listen = "0.0.0.0:1885" -next_connection_delay_ms = 10 - # Connection parameters - [servers.2.connections] - connection_timeout_ms = 100 - max_client_id_len = 256 - throttle_delay_ms = 0 - max_payload_size = 2048 - max_inflight_count = 100 - max_inflight_size = 1024 - # Certs for connection - [servers.2.certs] - cert_path = "tlsfiles/server.cert.pem" - key_path = "tlsfiles/server.key.pem" - ca_path = "tlsfiles/ca-chain.cert.pem" - -# Cluster configuration. Remote address to connect to. -# Mesh is created based on ids. -# 0 connects to 1 & 2 as client -# 1 connects to 2 as client and waits for 0 as a server -# 2 waits for 0 and 1 as a server -[cluster] - [cluster.0] - address = "localhost:1800" - [cluster.1] - address = "localhost:1801" - # [cluster.2] - # address = "localhost:1802" - -# Io configuration for replication -[replicator] -connection_timeout_ms = 100 -max_client_id_len = 256 -throttle_delay_ms = 0 -max_payload_size = 2048 -max_inflight_count = 500 -max_inflight_size = 1024 - -[console] -listen = "0.0.0.0:3032" diff --git a/rumqttd-old/docker/.gitignore b/rumqttd-old/docker/.gitignore deleted file mode 100644 index d3f531cac..000000000 --- a/rumqttd-old/docker/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# ignore everything except README in stage -stage/* -!stage/README.md diff --git a/rumqttd-old/docker/Dockerfile b/rumqttd-old/docker/Dockerfile deleted file mode 100644 index 0422f46c4..000000000 --- a/rumqttd-old/docker/Dockerfile +++ /dev/null @@ -1,7 +0,0 @@ -FROM ubuntu:20.04 - -COPY stage/rumqttd rumqttd -COPY stage/config config - -ENV RUST_LOG="info" -CMD ["./rumqttd"] diff --git a/rumqttd-old/docker/README.md b/rumqttd-old/docker/README.md deleted file mode 100644 index 22fa08346..000000000 --- a/rumqttd-old/docker/README.md +++ /dev/null @@ -1,11 +0,0 @@ - - -### Build and run ------------------- - -``` -./build.sh -docker run -it -p 1883:1883 --name rumqttd rumqttd # run foreground -docker run -d -p 1883:1883 --name rumqttd rumqttd # run background -docker run -d -p 1883:1883 -e "RUST_LOG=debug" --name rumqttd rumqttd # override loglevel -``` diff --git a/rumqttd-old/docker/build.sh b/rumqttd-old/docker/build.sh deleted file mode 100755 index 8d4da844d..000000000 --- a/rumqttd-old/docker/build.sh +++ /dev/null @@ -1,17 +0,0 @@ -#! /bin/sh - -set -ex - -cargo build --release - -docker stop rumqttd || true -docker rm rumqttd || true - -cp -r ../target/release/rumqttd stage/ -cp -r ../rumqttd/config stage/ - -docker build -t rumqttd . - -version=`awk -F ' = ' '$1 ~ /version/ { gsub(/[\"]/, "", $2); printf("%s",$2) }' ../rumqttd/Cargo.toml` -docker tag rumqttd:latest asia.gcr.io/bytebeam/rumqttd:$version -docker tag rumqttd:latest asia.gcr.io/bytebeam/rumqttd:latest diff --git a/rumqttd-old/docker/stage/README.md b/rumqttd-old/docker/stage/README.md deleted file mode 100644 index f099c286d..000000000 --- a/rumqttd-old/docker/stage/README.md +++ /dev/null @@ -1,2 +0,0 @@ - -Working directory to create the docker image diff --git a/rumqttd-old/examples/async_broker.rs b/rumqttd-old/examples/async_broker.rs deleted file mode 100644 index fdbb554f8..000000000 --- a/rumqttd-old/examples/async_broker.rs +++ /dev/null @@ -1,55 +0,0 @@ -use librumqttd::{async_locallink::construct_broker, Config}; -use std::thread; - -fn main() { - pretty_env_logger::init(); - let config: Config = confy::load_path("config/rumqttd.conf").unwrap(); - - let (mut router, console, servers, builder) = construct_broker(config); - - thread::spawn(move || { - router.start().unwrap(); - }); - - // connect to get a receiver - // TODO: Connect with a function which return tx and rx to prevent - // doing publishes before connecting - // NOTE: Connection buffer should be atleast total number of possible - // topics + 3 (request types). If inflight is full with more topics - // in tracker, it's possible that router never responnds current - // inflight requests. But other pending requests should still be able - // to progress - let mut rt = tokio::runtime::Builder::new_multi_thread(); - rt.enable_all(); - rt.build().unwrap().block_on(async { - let (mut tx, mut rx) = builder.connect("localclient", 200).await.unwrap(); - tx.subscribe(std::iter::once("#")).await.unwrap(); - - let console_task = tokio::spawn(console); - - // subscribe and publish in a separate thread - let pub_task = tokio::spawn(async move { - for _ in 0..10usize { - for i in 0..200usize { - let topic = format!("hello/{}/world", i); - tx.publish(topic, false, vec![0; 1024]).await.unwrap(); - } - } - }); - - let sub_task = tokio::spawn(async move { - let mut count = 0; - loop { - let message = rx.recv().await.unwrap(); - // println!("T = {}, P = {:?}", message.topic, message.payload.len()); - count += message.payload.len(); - println!("{}", count); - } - }); - - servers.await; - pub_task.await.unwrap(); - sub_task.await.unwrap(); - console_task.await.unwrap(); - }); -} diff --git a/rumqttd-old/examples/broker.rs b/rumqttd-old/examples/broker.rs deleted file mode 100644 index c947ac47c..000000000 --- a/rumqttd-old/examples/broker.rs +++ /dev/null @@ -1,43 +0,0 @@ -use librumqttd::{Broker, Config}; -use std::thread; - -fn main() { - pretty_env_logger::init(); - let config: Config = confy::load_path("config/rumqttd.conf").unwrap(); - let mut broker = Broker::new(config); - - let mut tx = broker.link("localclient").unwrap(); - thread::spawn(move || { - broker.start().unwrap(); - }); - - // connect to get a receiver - // TODO: Connect with a function which return tx and rx to prevent - // doing publishes before connecting - // NOTE: Connection buffer should be atleast total number of possible - // topics + 3 (request types). If inflight is full with more topics - // in tracker, it's possible that router never responnds current - // inflight requests. But other pending requests should still be able - // to progress - let mut rx = tx.connect(200).unwrap(); - tx.subscribe("#").unwrap(); - - // subscribe and publish in a separate thread - thread::spawn(move || { - for _ in 0..10 { - for i in 0..200 { - let topic = format!("hello/{}/world", i); - tx.publish(topic, false, vec![0; 1024]).unwrap(); - } - } - }); - - let mut count = 0; - loop { - if let Some(message) = rx.recv().unwrap() { - // println!("T = {}, P = {:?}", message.topic, message.payload.len()); - count += message.payload.len(); - println!("{}", count); - } - } -} diff --git a/rumqttd-old/rumqttd.test b/rumqttd-old/rumqttd.test deleted file mode 100644 index 2d3d74fe6a3b9589df8ee03e8586050d7d1dc7ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30709 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
_3_m%4}EQj3hU##k$ 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, - #[error("Channel recv error")] - Recv(#[from] RecvError), -} - -pub struct AsyncLinkTx { - id: Id, - router_tx: Sender<(Id, Event)>, -} - -impl AsyncLinkTx { - /// Sends a MQTT Publish to the router - pub async fn publish( - &mut self, - topic: S, - retain: bool, - payload: V, - ) -> Result<(), LinkError> - where - S: Into, - V: Into>, - { - let mut publish = Publish::new(topic, QoS::AtMostOnce, payload); - publish.retain = retain; - let message = Event::Data(vec![Packet::Publish(publish)]); - self.router_tx.async_send((self.id, message)).await?; - Ok(()) - } - - /// Sends a MQTT Subscribe to the eventloop - pub async fn subscribe>( - &mut self, - filters: impl IntoIterator, - ) -> Result<(), LinkError> { - let filters = filters - .into_iter() - .map(|topic_path| SubscribeFilter { - path: topic_path.into(), - qos: QoS::AtMostOnce, - }) - .collect(); - let subscribe = Subscribe { pkid: 0, filters }; - let packet = Packet::Subscribe(subscribe); - let message = Event::Data(vec![packet]); - self.router_tx.async_send((self.id, message)).await?; - Ok(()) - } -} - -pub struct AsyncLinkRx { - id: Id, - router_tx: Sender<(Id, Event)>, - link_rx: Receiver, -} - -impl AsyncLinkRx { - pub async fn recv(&mut self) -> Result { - loop { - let message = self.link_rx.async_recv().await?; - if let Some(message) = self.handle_router_response(message).await? { - return Ok(message); - } - } - } - - async fn handle_router_response( - &mut self, - message: Notification, - ) -> Result, LinkError> { - match message { - Notification::ConnectionAck(_) => unreachable!("ConnAck handled in connect"), - Notification::Message(_) => unreachable!("Local links are always clean"), - Notification::Data(reply) => { - trace!( - "{:11} {:14} Id = {}, Count = {}", - "data", - "reply", - self.id, - reply.payload.len() - ); - - Ok(Some(reply)) - } - Notification::Pause => { - let message = (self.id, Event::Ready); - self.router_tx.async_send(message).await?; - Ok(None) - } - notification => { - warn!("{:?} not supported in local link", notification); - Ok(None) - } - } - } -} - -#[derive(Clone)] -pub struct LinkBuilder { - router_tx: Sender<(Id, Event)>, -} - -impl LinkBuilder { - pub async fn connect( - self, - client_id: &str, - max_inflight_requests: usize, - ) -> Result<(AsyncLinkTx, AsyncLinkRx), LinkError> { - // connection queue capacity should match maximum inflight requests - let (connection, link_rx) = Connection::new_remote(client_id, true, max_inflight_requests); - - self.router_tx - .async_send((0, Event::Connect(connection))) - .await?; - - // Right now link identifies failure with dropped rx in router, which is probably ok - // We need this here to get id assigned by router - let id = match link_rx.async_recv().await? { - Notification::ConnectionAck(ConnectionAck::Success((id, _, _))) => Ok(id), - Notification::ConnectionAck(ConnectionAck::Failure(reason)) => { - Err(LinkError::ConnectionAck(reason)) - } - other => Err(LinkError::NotConnectionAck(other)), - }?; - - // Send initialization requests from tracker [topics request and acks request] - let rx = AsyncLinkRx { - id, - router_tx: self.router_tx.clone(), - link_rx, - }; - let tx = AsyncLinkTx { - id, - router_tx: self.router_tx, - }; - - Ok((tx, rx)) - } -} -/// Returns a Router struct to run router on a thread, console server async task, mqtt servers async task, and a LinkBuilder to make local links -pub fn construct_broker( - config: Config, -) -> ( - Router, - impl std::future::Future, - impl std::future::Future, - LinkBuilder, -) { - let (router, router_tx) = Router::new(Arc::new(config.router.clone())); - - let console = { - let config = config.clone().into(); - let router_tx = router_tx.clone(); - async { - // `ConsoleLink::new` won't terminate until router is running - let console = ConsoleLink::new(config, router_tx).into(); - consolelink::start(console).await; - } - }; - - let server = config - .servers - .into_iter() - .map(|(id, config)| { - let router_tx = router_tx.clone(); - async { - if let Err(e) = Server::new(id, config, router_tx).start().await { - error!("Accept loop error: {:?}", e); - } - } - }) - .collect::>() - .collect::<()>(); - - let builder = LinkBuilder { router_tx }; - - (router, console, server, builder) -} diff --git a/rumqttd-old/src/consolelink.rs b/rumqttd-old/src/consolelink.rs deleted file mode 100644 index be1019194..000000000 --- a/rumqttd-old/src/consolelink.rs +++ /dev/null @@ -1,79 +0,0 @@ -use crate::rumqttlog::ConnectionId; -use crate::rumqttlog::{ - Connection, ConnectionAck, Event, MetricsReply, MetricsRequest, Notification, Receiver, Sender, -}; -use crate::Config; -use std::sync::Arc; -use warp::Filter; - -pub struct ConsoleLink { - config: Arc, - id: ConnectionId, - router_tx: Sender<(ConnectionId, Event)>, - link_rx: Receiver, -} - -impl ConsoleLink { - /// Requires the corresponding Router to be running to complete - pub fn new(config: Arc, router_tx: Sender<(ConnectionId, Event)>) -> ConsoleLink { - let (connection, link_rx) = Connection::new_remote("console", true, 10); - let message = (0, Event::Connect(connection)); - router_tx.send(message).unwrap(); - - let (id, _, _) = match link_rx.recv().unwrap() { - Notification::ConnectionAck(ack) => match ack { - ConnectionAck::Success((id, session, pending)) => (id, session, pending), - ConnectionAck::Failure(reason) => unreachable!("{}", reason), - }, - notification => unreachable!("{:?}", notification), - }; - - ConsoleLink { - config, - id, - router_tx, - link_rx, - } - } -} - -pub async fn start(console: Arc) { - let config_console = console.clone(); - let address = config_console.config.console.listen; - - let config = warp::path!("node" / "config").map(move || { - let config = config_console.config.clone(); - warp::reply::json(config.as_ref()) - }); - - let router_console = console.clone(); - let router = warp::path!("node" / "router").map(move || { - let message = Event::Metrics(MetricsRequest::Router); - router_console - .router_tx - .send((router_console.id, message)) - .unwrap(); - - match router_console.link_rx.recv().unwrap() { - Notification::Metrics(MetricsReply::Router(v)) => warp::reply::json(&v), - v => unreachable!("{:?}", v), - } - }); - - let connection_console = console.clone(); - let connection = warp::path!("node" / String).map(move |id| { - let message = Event::Metrics(MetricsRequest::Connection(id)); - connection_console - .router_tx - .send((connection_console.id, message)) - .unwrap(); - - match connection_console.link_rx.recv().unwrap() { - Notification::Metrics(MetricsReply::Connection(v)) => warp::reply::json(&v), - v => unreachable!("{:?}", v), - } - }); - - let routes = warp::get().and(config.or(router).or(connection)); - warp::serve(routes).run(address).await; -} diff --git a/rumqttd-old/src/lib.rs b/rumqttd-old/src/lib.rs deleted file mode 100644 index 4ed4e6c7c..000000000 --- a/rumqttd-old/src/lib.rs +++ /dev/null @@ -1,536 +0,0 @@ -use jackiechan::Sender; -use log::{error, info}; -use serde::{Deserialize, Serialize}; -use std::time::Duration; -use std::{io, thread}; -use std::{net::SocketAddr, sync::Arc}; - -mod mqttbytes; -pub mod rumqttlog; - -use mqttbytes::v4::Packet; -use rumqttlog::{Disconnection, Event, RecvError, Router, SendError}; -use tokio::time::error::Elapsed; - -use crate::remotelink::RemoteLink; - -use tokio::io::{AsyncRead, AsyncWrite}; -use tokio::net::TcpListener; -use tokio::{task, time}; - -// All requirements for `rustls` -#[cfg(feature = "use-rustls")] -use rustls_pemfile::{certs, rsa_private_keys}; -#[cfg(feature = "use-rustls")] -use tokio_rustls::rustls::{ - server::AllowAnyAuthenticatedClient, Certificate, Error as RustlsError, PrivateKey, -}; - -// All requirements for `native-tls` -#[cfg(feature = "use-native-tls")] -use std::io::Read; -#[cfg(feature = "use-native-tls")] -use tokio_native_tls::native_tls; -#[cfg(feature = "use-native-tls")] -use tokio_native_tls::native_tls::Error as NativeTlsError; -pub mod async_locallink; -mod consolelink; -mod locallink; -mod network; -mod remotelink; -mod state; - -use crate::consolelink::ConsoleLink; -pub use crate::locallink::{LinkError, LinkRx, LinkTx}; -use crate::network::Network; -#[cfg(feature = "use-rustls")] -use crate::Error::ServerKeyNotFound; -use std::collections::HashMap; - -#[cfg(any(feature = "use-rustls", feature = "use-native-tls"))] -use std::fs::File; -#[cfg(feature = "use-rustls")] -use std::io::BufReader; - -#[derive(Debug, thiserror::Error)] -#[error("Acceptor error")] -pub enum Error { - #[error("I/O: {0}")] - Io(#[from] io::Error), - #[error("Connection error: {0}")] - Connection(#[from] remotelink::Error), - #[error("Timeout")] - Timeout(#[from] Elapsed), - #[error("Channel recv error")] - Recv(#[from] RecvError), - #[error("Channel send error")] - Send(#[from] SendError<(Id, Event)>), - #[cfg(feature = "use-native-tls")] - #[error("Native TLS error: {0}")] - NativeTls(#[from] NativeTlsError), - #[cfg(feature = "use-rustls")] - #[error("Rustls error: {0}")] - Rustls(#[from] RustlsError), - #[error("Server cert not provided")] - ServerCertRequired, - #[error("Server private key not provided")] - ServerKeyRequired, - #[error("CA file {0} no found")] - CaFileNotFound(String), - #[error("Server cert file {0} not found")] - ServerCertNotFound(String), - #[error("Server private key file {0} not found")] - ServerKeyNotFound(String), - #[error("Invalid CA cert file: {0}")] - InvalidCACert(String), - #[error("Invalid server cert file: {0}")] - InvalidServerCert(String), - #[error("Invalid server pass")] - InvalidServerPass, - #[error("Invalid server key file: {0}")] - InvalidServerKey(String), - RustlsNotEnabled, - NativeTlsNotEnabled, - Disconnected, - NetworkClosed, - #[error("Wrong packet: {0:?}")] - WrongPacket(Packet), -} - -type Id = usize; - -#[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct Config { - pub id: usize, - pub router: rumqttlog::Config, - pub servers: HashMap, - pub cluster: Option>, - pub replicator: Option, - pub console: ConsoleSettings, -} - -#[allow(dead_code)] -enum ServerTLSAcceptor { - #[cfg(feature = "use-rustls")] - RustlsAcceptor { acceptor: tokio_rustls::TlsAcceptor }, - #[cfg(feature = "use-native-tls")] - NativeTLSAcceptor { - acceptor: tokio_native_tls::TlsAcceptor, - }, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -#[serde(untagged)] -pub enum ServerCert { - RustlsCert { - ca_path: String, - cert_path: String, - key_path: String, - }, - NativeTlsCert { - pkcs12_path: String, - pkcs12_pass: String, - }, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct ServerSettings { - pub listen: SocketAddr, - pub cert: Option, - pub next_connection_delay_ms: u64, - pub connections: ConnectionSettings, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct ConnectionLoginCredentials { - pub username: String, - pub password: String, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct ConnectionSettings { - pub connection_timeout_ms: u16, - pub max_client_id_len: usize, - pub throttle_delay_ms: u64, - pub max_payload_size: usize, - pub max_inflight_count: u16, - pub max_inflight_size: usize, - pub login_credentials: Option>, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct MeshSettings { - pub address: SocketAddr, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ConsoleSettings { - pub listen: SocketAddr, -} - -impl Default for ServerSettings { - fn default() -> Self { - panic!("Server settings should be derived from a configuration file") - } -} - -impl Default for ConnectionSettings { - fn default() -> Self { - panic!("Server settings should be derived from a configuration file") - } -} - -impl Default for ConsoleSettings { - fn default() -> Self { - panic!("Console settings should be derived from configuration file") - } -} - -pub struct Broker { - config: Arc, - router_tx: Sender<(Id, Event)>, - router: Option, -} - -impl Broker { - pub fn new(config: Config) -> Broker { - let config = Arc::new(config); - let router_config = Arc::new(config.router.clone()); - let (router, router_tx) = Router::new(router_config); - Broker { - config, - router_tx, - router: Some(router), - } - } - - pub fn router_handle(&self) -> Sender<(Id, Event)> { - self.router_tx.clone() - } - - pub fn link(&self, client_id: &str) -> Result { - // Register this connection with the router. Router replies with ack which if ok will - // start the link. Router can sometimes reject the connection (ex max connection limit) - - let tx = LinkTx::new(client_id, self.router_tx.clone()); - Ok(tx) - } - - pub fn start(&mut self) -> Result<(), Error> { - // spawn the router in a separate thread - let mut router = self.router.take().unwrap(); - let router_thread = thread::Builder::new().name("rumqttd-router".to_owned()); - router_thread.spawn(move || router.start())?; - - // spawn servers in a separate thread - for (id, config) in self.config.servers.clone() { - let server_name = format!("rumqttd-server-{}", id); - let server_thread = thread::Builder::new().name(server_name); - let server = Server::new(id, config, self.router_tx.clone()); - server_thread.spawn(move || { - let mut runtime = tokio::runtime::Builder::new_current_thread(); - let runtime = runtime.enable_all().build().unwrap(); - runtime.block_on(async { - if let Err(e) = server.start().await { - error!("Stopping server. Accept loop error: {:?}", e.to_string()); - } - }); - })?; - } - - let mut runtime = tokio::runtime::Builder::new_current_thread(); - let runtime = runtime.enable_all().build().unwrap(); - - // Run console in current thread, if it is configured. - let console = ConsoleLink::new(self.config.clone(), self.router_tx.clone()); - let console = Arc::new(console); - runtime.block_on(async { - consolelink::start(console).await; - }); - - Ok(()) - } -} - -struct Server { - id: String, - config: ServerSettings, - router_tx: Sender<(Id, Event)>, -} - -impl Server { - pub fn new(id: String, config: ServerSettings, router_tx: Sender<(Id, Event)>) -> Server { - Server { - id, - config, - router_tx, - } - } - - #[cfg(feature = "use-native-tls")] - fn tls_native_tls( - &self, - pkcs12_path: &str, - pkcs12_pass: &str, - ) -> Result, Error> { - // Get certificates - let cert_file = File::open(&pkcs12_path); - let mut cert_file = - cert_file.map_err(|_| Error::ServerCertNotFound(pkcs12_path.to_string()))?; - - // Read cert into memory - let mut buf = Vec::new(); - cert_file - .read_to_end(&mut buf) - .map_err(|_| Error::InvalidServerCert(pkcs12_path.to_string()))?; - - // Get the identity - let identity = native_tls::Identity::from_pkcs12(&buf, pkcs12_pass) - .map_err(|_| Error::InvalidServerPass)?; - - // Builder - let builder = native_tls::TlsAcceptor::builder(identity).build()?; - - // Create acceptor - let acceptor = tokio_native_tls::TlsAcceptor::from(builder); - Ok(Some(ServerTLSAcceptor::NativeTLSAcceptor { acceptor })) - } - - #[allow(dead_code)] - #[cfg(not(feature = "use-native-tls"))] - fn tls_native_tls( - &self, - _pkcs12_path: &str, - _pkcs12_pass: &str, - ) -> Result, Error> { - Err(Error::NativeTlsNotEnabled) - } - - #[cfg(feature = "use-rustls")] - fn tls_rustls( - &self, - cert_path: &str, - key_path: &str, - ca_path: &str, - ) -> Result, Error> { - use tokio_rustls::rustls::{RootCertStore, ServerConfig}; - - let (certs, key) = { - // Get certificates - let cert_file = File::open(&cert_path); - let cert_file = - cert_file.map_err(|_| Error::ServerCertNotFound(cert_path.to_owned()))?; - let certs = certs(&mut BufReader::new(cert_file)); - let certs = certs.map_err(|_| Error::InvalidServerCert(cert_path.to_string()))?; - let certs = certs - .iter() - .map(|cert| Certificate(cert.to_owned())) - .collect(); - - // Get private key - let key_file = File::open(&key_path); - let key_file = key_file.map_err(|_| ServerKeyNotFound(key_path.to_owned()))?; - let keys = rsa_private_keys(&mut BufReader::new(key_file)); - let keys = keys.map_err(|_| Error::InvalidServerKey(key_path.to_owned()))?; - - // Get the first key - let key = match keys.first() { - Some(k) => k.clone(), - None => return Err(Error::InvalidServerKey(key_path.to_owned())), - }; - - (certs, PrivateKey(key)) - }; - - // client authentication with a CA. CA isn't required otherwise - let server_config = { - let ca_file = File::open(ca_path); - let ca_file = ca_file.map_err(|_| Error::CaFileNotFound(ca_path.to_owned()))?; - let ca_file = &mut BufReader::new(ca_file); - let ca_certs = rustls_pemfile::certs(ca_file)?; - let ca_cert = ca_certs - .first() - .map(|c| Certificate(c.to_owned())) - .ok_or_else(|| Error::InvalidCACert(ca_path.to_string()))?; - let mut store = RootCertStore::empty(); - store - .add(&ca_cert) - .map_err(|_| Error::InvalidCACert(ca_path.to_string()))?; - ServerConfig::builder() - .with_safe_defaults() - .with_client_cert_verifier(AllowAnyAuthenticatedClient::new(store)) - .with_single_cert(certs, key)? - }; - - let acceptor = tokio_rustls::TlsAcceptor::from(Arc::new(server_config)); - Ok(Some(ServerTLSAcceptor::RustlsAcceptor { acceptor })) - } - - #[allow(dead_code)] - #[cfg(not(feature = "use-rustls"))] - fn tls_rustls( - &self, - _cert_path: &str, - _key_path: &str, - _ca_path: &str, - ) -> Result, Error> { - Err(Error::RustlsNotEnabled) - } - - async fn start(&self) -> Result<(), Error> { - let listener = TcpListener::bind(&self.config.listen).await?; - let delay = Duration::from_millis(self.config.next_connection_delay_ms); - let mut count = 0; - - let config = Arc::new(self.config.connections.clone()); - - // Get the ServerTLSAcceptor which allow us to use either Rustls or Native TLS - #[cfg(any(feature = "use-rustls", feature = "use-native-tls"))] - let acceptor = match &self.config.cert { - Some(c) => match c { - ServerCert::RustlsCert { - ca_path, - cert_path, - key_path, - } => self.tls_rustls(cert_path, key_path, ca_path)?, - ServerCert::NativeTlsCert { - pkcs12_path, - pkcs12_pass, - } => self.tls_native_tls(pkcs12_path, pkcs12_pass)?, - }, - None => None, - }; - - let max_incoming_size = config.max_payload_size; - - info!( - "Waiting for connections on {}. Server = {}", - self.config.listen, self.id - ); - loop { - // Await new network connection. - let (stream, addr) = match listener.accept().await { - Ok((s, r)) => (s, r), - Err(_e) => { - error!("Unable to accept socket."); - continue; - } - }; - - // Depending on TLS or not create a new Network - #[cfg(any(feature = "use-rustls", feature = "use-native-tls"))] - let network = match &acceptor { - Some(a) => { - info!("{}. Accepting TLS connection from: {}", count, addr); - - // Depending on which acceptor we're using address accordingly.. - match a { - #[cfg(feature = "use-rustls")] - ServerTLSAcceptor::RustlsAcceptor { acceptor } => { - let stream = match acceptor.accept(stream).await { - Ok(v) => v, - Err(e) => { - error!("Failed to accept TLS connection using Rustls. Error = {:?}", e); - continue; - } - }; - - Network::new(stream, max_incoming_size) - } - #[cfg(feature = "use-native-tls")] - ServerTLSAcceptor::NativeTLSAcceptor { acceptor } => { - let stream = match acceptor.accept(stream).await { - Ok(v) => v, - Err(e) => { - error!("Failed to accept TLS connection using Native TLS. Error = {:?}", e); - continue; - } - }; - - Network::new(stream, max_incoming_size) - } - } - } - None => { - info!("{}. Accepting TCP connection from: {}", count, addr); - Network::new(stream, max_incoming_size) - } - }; - #[cfg(not(any(feature = "use-rustls", feature = "use-native-tls")))] - let network = { - info!("{}. Accepting TCP connection from: {}", count, addr); - Network::new(stream, max_incoming_size) - }; - - count += 1; - - let config = config.clone(); - let router_tx = self.router_tx.clone(); - - // Spawn a new thread to handle this connection. - task::spawn(async { - let connector = Connector::new(config, router_tx); - if let Err(e) = connector.new_connection(network).await { - error!("Dropping link task!! Result = {:?}", e); - } - }); - - time::sleep(delay).await; - } - } -} - -struct Connector { - config: Arc, - router_tx: Sender<(Id, Event)>, -} - -impl Connector { - fn new(config: Arc, router_tx: Sender<(Id, Event)>) -> Connector { - Connector { config, router_tx } - } - - /// A new network connection should wait for mqtt connect packet. This handling should be handled - /// asynchronously to avoid listener from not blocking new connections while this connection is - /// waiting for mqtt connect packet. Also this honours connection wait time as per config to prevent - /// denial of service attacks (rogue clients which only does network connection without sending - /// mqtt connection packet to make make the server reach its concurrent connection limit) - async fn new_connection(&self, network: Network) -> Result<(), Error> { - let config = self.config.clone(); - let router_tx = self.router_tx.clone(); - - // Start the link - let (client_id, id, mut link) = RemoteLink::new(config, router_tx, network).await?; - let (execute_will, pending) = match link.start().await { - // Connection get close. This shouldn't usually happen - Ok(_) => { - error!("Stopped!! Id = {} ({})", client_id, id); - (true, link.state.clean()) - } - // We are representing clean close as Abort in `Network` - Err(remotelink::Error::Io(e)) if e.kind() == io::ErrorKind::ConnectionAborted => { - info!("Closed!! Id = {} ({})", client_id, id); - (true, link.state.clean()) - } - // Client requested disconnection. - Err(remotelink::Error::Disconnect) => { - info!("Disconnected!! Id = {} ({})", client_id, id); - (false, link.state.clean()) - } - // Any other error - Err(e) => { - error!("Error!! Id = {} ({}), {}", client_id, id, e.to_string()); - (true, link.state.clean()) - } - }; - - let disconnect = Disconnection::new(client_id, execute_will, pending); - let disconnect = Event::Disconnect(disconnect); - let message = (id, disconnect); - self.router_tx.send(message)?; - Ok(()) - } -} - -pub trait IO: AsyncRead + AsyncWrite + Send + Sync + Unpin {} -impl IO for T {} diff --git a/rumqttd-old/src/locallink.rs b/rumqttd-old/src/locallink.rs deleted file mode 100644 index 2a14f4daa..000000000 --- a/rumqttd-old/src/locallink.rs +++ /dev/null @@ -1,153 +0,0 @@ -use log::{trace, warn}; - -use crate::mqttbytes::v4::*; -use crate::mqttbytes::*; -use crate::rumqttlog::{ - Connection, ConnectionAck, Data, Event, Notification, Receiver, RecvError, RecvTimeoutError, - SendError, Sender, -}; -use crate::Id; -use std::time::Instant; - -#[derive(Debug, thiserror::Error)] -pub enum LinkError { - #[error("Unexpected router message: {0:?}")] - NotConnectionAck(Notification), - #[error("Connack error: {0}")] - ConnectionAck(String), - #[error("Channel send error")] - Send(#[from] SendError<(Id, Event)>), - #[error("Channel recv error")] - Recv(#[from] RecvError), - #[error("Channel timeout recv error")] - RecvTimeout(#[from] RecvTimeoutError), -} - -pub struct LinkTx { - id: Id, - router_tx: Sender<(Id, Event)>, - client_id: String, -} - -impl LinkTx { - pub(crate) fn new(client_id: impl Into, router_tx: Sender<(Id, Event)>) -> Self { - Self { - id: 0, - router_tx, - client_id: client_id.into(), - } - } - - pub fn connect(&mut self, max_inflight_requests: usize) -> Result { - // connection queue capacity should match that maximum inflight requests - let (connection, link_rx) = - Connection::new_remote(&self.client_id, true, max_inflight_requests); - - let message = (0, Event::Connect(connection)); - self.router_tx.send(message).unwrap(); - - // Right now link identifies failure with dropped rx in router, which is probably ok - // We need this here to get id assigned by router - match link_rx.recv()? { - Notification::ConnectionAck(ack) => match ack { - ConnectionAck::Success((id, _, _)) => self.id = id, - ConnectionAck::Failure(reason) => return Err(LinkError::ConnectionAck(reason)), - }, - message => return Err(LinkError::NotConnectionAck(message)), - }; - - // Send initialization requests from tracker [topics request and acks request] - let rx = LinkRx::new(self.id, self.router_tx.clone(), link_rx); - - Ok(rx) - } - - /// Sends a MQTT Publish to the router - pub fn publish(&mut self, topic: S, retain: bool, payload: V) -> Result<(), LinkError> - where - S: Into, - V: Into>, - { - let mut publish = Publish::new(topic, QoS::AtMostOnce, payload); - publish.retain = retain; - let message = Event::Data(vec![Packet::Publish(publish)]); - self.router_tx.send((self.id, message))?; - Ok(()) - } - - /// Sends a MQTT Subscribe to the eventloop - pub fn subscribe>(&mut self, filter: S) -> Result<(), LinkError> { - let subscribe = Subscribe::new(filter.into(), QoS::AtMostOnce); - let packet = Packet::Subscribe(subscribe); - let message = Event::Data(vec![packet]); - self.router_tx.send((self.id, message))?; - Ok(()) - } -} - -pub struct LinkRx { - id: Id, - router_tx: Sender<(Id, Event)>, - link_rx: Receiver, -} - -impl LinkRx { - pub(crate) fn new( - id: Id, - router_tx: Sender<(Id, Event)>, - link_rx: Receiver, - ) -> Self { - Self { - id, - router_tx, - link_rx, - } - } - - pub fn recv(&mut self) -> Result, LinkError> { - let message = self.link_rx.recv()?; - let message = self.handle_router_response(message)?; - Ok(message) - } - - pub fn recv_deadline(&mut self, deadline: Instant) -> Result, LinkError> { - let message = self.link_rx.recv_deadline(deadline)?; - let message = self.handle_router_response(message)?; - Ok(message) - } - - pub async fn async_recv(&mut self) -> Result, LinkError> { - let message = self.link_rx.async_recv().await?; - let message = self.handle_router_response(message)?; - Ok(message) - } - - fn handle_router_response(&mut self, message: Notification) -> Result, LinkError> { - match message { - Notification::ConnectionAck(_) => Ok(None), - Notification::Message(_) => { - unreachable!("Local links are always clean"); - } - Notification::Data(reply) => { - trace!( - "{:11} {:14} Id = {}, Count = {}", - "data", - "reply", - self.id, - reply.payload.len() - ); - - Ok(Some(reply)) - } - Notification::Pause => { - let message = (self.id, Event::Ready); - self.router_tx.send(message)?; - Ok(None) - } - notification => { - warn!("{:?} not supported in local link", notification); - Ok(None) - } - } - } -} diff --git a/rumqttd-old/src/main.rs b/rumqttd-old/src/main.rs deleted file mode 100644 index 30d369360..000000000 --- a/rumqttd-old/src/main.rs +++ /dev/null @@ -1,61 +0,0 @@ -use argh::FromArgs; -use librumqttd::{Broker, Config}; - -use std::path::PathBuf; - -#[cfg(not(target_env = "msvc"))] -#[global_allocator] -static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; - -#[derive(FromArgs, Debug)] -/// Command line args for rumqttd -struct CommandLine { - /// path to config file - #[argh( - option, - short = 'c', - default = "PathBuf::from(\"config/rumqttd.conf\")" - )] - config: PathBuf, - #[cfg(all(feature = "prof", not(target_env = "msvc")))] - /// enable profiling - #[argh(switch, short = 'p')] - profile: bool, -} - -fn main() { - pretty_env_logger::init(); - let commandline: CommandLine = argh::from_env(); - let config: Config = confy::load_path(&commandline.config).unwrap(); - - #[cfg(all(feature = "prof", not(target_env = "msvc")))] - let _guard = prof::new(commandline.profile); - - let o = Broker::new(config).start(); - println!("Stopping broker!! Error = {:?}", o); -} - -#[cfg(all(feature = "prof", not(target_env = "msvc")))] -mod prof { - use pprof::protos::Message; - pub struct Profile(pprof::ProfilerGuard<'static>); - pub fn new(enable: bool) -> Option { - if enable { - Some(Profile(pprof::ProfilerGuard::new(100).unwrap())) - } else { - None - } - } - impl Drop for Profile { - fn drop(&mut self) { - if let Ok(report) = self.0.report().build() { - let profile = report.pprof().unwrap(); - let mut buf = Vec::with_capacity(profile.encoded_len()); - profile.encode(&mut buf).unwrap(); - let path = "rumqttd.pb"; - std::fs::write(path, &buf).unwrap(); - println!("wrote profile to {}", path); - } - } - } -} diff --git a/rumqttd-old/src/mqttbytes/mod.rs b/rumqttd-old/src/mqttbytes/mod.rs deleted file mode 100644 index 494980333..000000000 --- a/rumqttd-old/src/mqttbytes/mod.rs +++ /dev/null @@ -1,335 +0,0 @@ -//! # mqttbytes -//! -//! This module contains the low level struct definitions required to assemble and disassemble MQTT 3.1.1 packets in rumqttd. -//! The [`bytes`](https://docs.rs/bytes) crate is used internally. - -use bytes::{Buf, BufMut, Bytes, BytesMut}; -use core::fmt; -use std::slice::Iter; - -mod topic; -pub mod v4; - -pub use topic::*; - -/// Error during serialization and deserialization -#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)] -pub enum Error { - #[error("Expected Connect, received: {0:?}")] - NotConnect(PacketType), - #[error("Unexpected Connect")] - UnexpectedConnect, - #[error("Invalid Connect return code: {0}")] - InvalidConnectReturnCode(u8), - #[error("Invalid protocol")] - InvalidProtocol, - #[error("Invalid protocol level: {0}")] - InvalidProtocolLevel(u8), - #[error("Incorrect packet format")] - IncorrectPacketFormat, - #[error("Invalid packet type: {0}")] - InvalidPacketType(u8), - #[error("Invalid property type: {0}")] - InvalidPropertyType(u8), - #[error("Invalid QoS level: {0}")] - InvalidQoS(u8), - #[error("Invalid subscribe reason code: {0}")] - InvalidSubscribeReasonCode(u8), - #[error("Packet id Zero")] - PacketIdZero, - #[error("Payload size is incorrect")] - PayloadSizeIncorrect, - #[error("payload is too long")] - PayloadTooLong, - #[error("payload size limit exceeded: {0}")] - PayloadSizeLimitExceeded(usize), - #[error("Payload required")] - PayloadRequired, - #[error("Topic is not UTF-8")] - TopicNotUtf8, - #[error("Promised boundary crossed: {0}")] - BoundaryCrossed(usize), - #[error("Malformed packet")] - MalformedPacket, - #[error("Malformed remaining length")] - MalformedRemainingLength, - /// More bytes required to frame packet. Argument - /// implies minimum additional bytes required to - /// proceed further - #[error("At least {0} more bytes required to frame packet")] - InsufficientBytes(usize), -} - -/// MQTT packet type -#[repr(u8)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum PacketType { - Connect = 1, - ConnAck, - Publish, - PubAck, - PubRec, - PubRel, - PubComp, - Subscribe, - SubAck, - Unsubscribe, - UnsubAck, - PingReq, - PingResp, - Disconnect, -} - -/// Protocol type -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Protocol { - V4, - V5, -} - -/// Quality of service -#[repr(u8)] -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)] -#[allow(clippy::enum_variant_names)] -pub enum QoS { - AtMostOnce = 0, - AtLeastOnce = 1, - ExactlyOnce = 2, -} - -/// Packet type from a byte -/// -/// ```text -/// 7 3 0 -/// +--------------------------+--------------------------+ -/// byte 1 | MQTT Control Packet Type | Flags for each type | -/// +--------------------------+--------------------------+ -/// | Remaining Bytes Len (1/2/3/4 bytes) | -/// +-----------------------------------------------------+ -/// -/// -/// ``` -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)] -pub struct FixedHeader { - /// First byte of the stream. Used to identify packet types and - /// several flags - byte1: u8, - /// Length of fixed header. Byte 1 + (1..4) bytes. So fixed header - /// len can vary from 2 bytes to 5 bytes - /// 1..4 bytes are variable length encoded to represent remaining length - fixed_header_len: usize, - /// Remaining length of the packet. Doesn't include fixed header bytes - /// Represents variable header + payload size - remaining_len: usize, -} - -impl FixedHeader { - pub fn new(byte1: u8, remaining_len_len: usize, remaining_len: usize) -> FixedHeader { - FixedHeader { - byte1, - fixed_header_len: remaining_len_len + 1, - remaining_len, - } - } - - pub fn packet_type(&self) -> Result { - let num = self.byte1 >> 4; - match num { - 1 => Ok(PacketType::Connect), - 2 => Ok(PacketType::ConnAck), - 3 => Ok(PacketType::Publish), - 4 => Ok(PacketType::PubAck), - 5 => Ok(PacketType::PubRec), - 6 => Ok(PacketType::PubRel), - 7 => Ok(PacketType::PubComp), - 8 => Ok(PacketType::Subscribe), - 9 => Ok(PacketType::SubAck), - 10 => Ok(PacketType::Unsubscribe), - 11 => Ok(PacketType::UnsubAck), - 12 => Ok(PacketType::PingReq), - 13 => Ok(PacketType::PingResp), - 14 => Ok(PacketType::Disconnect), - _ => Err(Error::InvalidPacketType(num)), - } - } - - /// Returns the size of full packet (fixed header + variable header + payload) - /// Fixed header is enough to get the size of a frame in the stream - pub fn frame_length(&self) -> usize { - self.fixed_header_len + self.remaining_len - } -} - -/// Checks if the stream has enough bytes to frame a packet and returns fixed header -/// only if a packet can be framed with existing bytes in the `stream`. -/// The passed stream doesn't modify parent stream's cursor. If this function -/// returned an error, next `check` on the same parent stream is forced start -/// with cursor at 0 again (Iter is owned. Only Iter's cursor is changed internally) -pub fn check(stream: Iter, max_packet_size: usize) -> Result { - // Create fixed header if there are enough bytes in the stream - // to frame full packet - let stream_len = stream.len(); - let fixed_header = parse_fixed_header(stream)?; - - // Don't let rogue connections attack with huge payloads. - // Disconnect them before reading all that data - if fixed_header.remaining_len > max_packet_size { - return Err(Error::PayloadSizeLimitExceeded(fixed_header.remaining_len)); - } - - // If the current call fails due to insufficient bytes in the stream, - // after calculating remaining length, we extend the stream - let frame_length = fixed_header.frame_length(); - if stream_len < frame_length { - return Err(Error::InsufficientBytes(frame_length - stream_len)); - } - - Ok(fixed_header) -} - -/// Parses fixed header -fn parse_fixed_header(mut stream: Iter) -> Result { - // At least 2 bytes are necessary to frame a packet - let stream_len = stream.len(); - if stream_len < 2 { - return Err(Error::InsufficientBytes(2 - stream_len)); - } - - let byte1 = stream.next().unwrap(); - let (len_len, len) = length(stream)?; - - Ok(FixedHeader::new(*byte1, len_len, len)) -} - -/// Parses variable byte integer in the stream and returns the length -/// and number of bytes that make it. Used for remaining length calculation -/// as well as for calculating property lengths -fn length(stream: Iter) -> Result<(usize, usize), Error> { - let mut len: usize = 0; - let mut len_len = 0; - let mut done = false; - let mut shift = 0; - - // Use continuation bit at position 7 to continue reading next - // byte to frame 'length'. - // Stream 0b1xxx_xxxx 0b1yyy_yyyy 0b1zzz_zzzz 0b0www_wwww will - // be framed as number 0bwww_wwww_zzz_zzzz_yyy_yyyy_xxx_xxxx - for byte in stream { - len_len += 1; - let byte = *byte as usize; - len += (byte & 0x7F) << shift; - - // stop when continue bit is 0 - done = (byte & 0x80) == 0; - if done { - break; - } - - shift += 7; - - // Only a max of 4 bytes allowed for remaining length - // more than 4 shifts (0, 7, 14, 21) implies bad length - if shift > 21 { - return Err(Error::MalformedRemainingLength); - } - } - - // Not enough bytes to frame remaining length. wait for - // one more byte - if !done { - return Err(Error::InsufficientBytes(1)); - } - - Ok((len_len, len)) -} - -/// Reads a series of bytes with a length from a byte stream -fn read_mqtt_bytes(stream: &mut Bytes) -> Result { - let len = read_u16(stream)? as usize; - - // Prevent attacks with wrong remaining length. This method is used in - // `packet.assembly()` with (enough) bytes to frame packet. Ensures that - // reading variable len string or bytes doesn't cross promised boundary - // with `read_fixed_header()` - if len > stream.len() { - return Err(Error::BoundaryCrossed(len)); - } - - Ok(stream.split_to(len)) -} - -/// Reads a string from bytes stream -fn read_mqtt_string(stream: &mut Bytes) -> Result { - let s = read_mqtt_bytes(stream)?; - match String::from_utf8(s.to_vec()) { - Ok(v) => Ok(v), - Err(_e) => Err(Error::TopicNotUtf8), - } -} - -/// Serializes bytes to stream (including length) -fn write_mqtt_bytes(stream: &mut BytesMut, bytes: &[u8]) { - stream.put_u16(bytes.len() as u16); - stream.extend_from_slice(bytes); -} - -/// Serializes a string to stream -fn write_mqtt_string(stream: &mut BytesMut, string: &str) { - write_mqtt_bytes(stream, string.as_bytes()); -} - -/// Writes remaining length to stream and returns number of bytes for remaining length -fn write_remaining_length(stream: &mut BytesMut, len: usize) -> Result { - if len > 268_435_455 { - return Err(Error::PayloadTooLong); - } - - let mut done = false; - let mut x = len; - let mut count = 0; - - while !done { - let mut byte = (x % 128) as u8; - x /= 128; - if x > 0 { - byte |= 128; - } - - stream.put_u8(byte); - count += 1; - done = x == 0; - } - - Ok(count) -} - -/// Maps a number to QoS -pub fn qos(num: u8) -> Result { - match num { - 0 => Ok(QoS::AtMostOnce), - 1 => Ok(QoS::AtLeastOnce), - 2 => Ok(QoS::ExactlyOnce), - qos => Err(Error::InvalidQoS(qos)), - } -} - -/// After collecting enough bytes to frame a packet (packet's frame()) -/// , It's possible that content itself in the stream is wrong. Like expected -/// packet id or qos not being present. In cases where `read_mqtt_string` or -/// `read_mqtt_bytes` exhausted remaining length but packet framing expects to -/// parse qos next, these pre checks will prevent `bytes` crashes -fn read_u16(stream: &mut Bytes) -> Result { - if stream.len() < 2 { - return Err(Error::MalformedPacket); - } - - Ok(stream.get_u16()) -} - -fn read_u8(stream: &mut Bytes) -> Result { - if stream.is_empty() { - return Err(Error::MalformedPacket); - } - - Ok(stream.get_u8()) -} diff --git a/rumqttd-old/src/mqttbytes/topic.rs b/rumqttd-old/src/mqttbytes/topic.rs deleted file mode 100644 index 9f1a6451d..000000000 --- a/rumqttd-old/src/mqttbytes/topic.rs +++ /dev/null @@ -1,125 +0,0 @@ -/// Checks if a topic or topic filter has wildcards -pub fn has_wildcards(s: &str) -> bool { - s.contains('+') || s.contains('#') -} - -/// Checks if topic matches a filter. topic and filter validation isn't done here. -/// -/// **NOTE**: 'topic' is a misnomer in the arg. this can also be used to match 2 wild subscriptions -/// **NOTE**: make sure a topic is validated during a publish and filter is validated -/// during a subscribe -pub fn matches(topic: &str, filter: &str) -> bool { - if !topic.is_empty() && topic[..1].contains('$') { - return false; - } - - let mut topics = topic.split('/'); - let mut filters = filter.split('/'); - - for f in filters.by_ref() { - // "#" being the last element is validated by the broker with 'valid_filter' - if f == "#" { - return true; - } - - // filter still has remaining elements - // filter = a/b/c/# should match topci = a/b/c - // filter = a/b/c/d should not match topic = a/b/c - let top = topics.next(); - match top { - Some(t) if t == "#" => return false, - Some(_) if f == "+" => continue, - Some(t) if f != t => return false, - Some(_) => continue, - None => return false, - } - } - - // topic has remaining elements and filter's last element isn't "#" - if topics.next().is_some() { - return false; - } - - true -} - -#[cfg(test)] -mod test { - #[test] - fn wildcards_are_detected_correctly() { - assert!(!super::has_wildcards("a/b/c")); - assert!(super::has_wildcards("a/+/c")); - assert!(super::has_wildcards("a/b/#")); - } - - #[test] - fn dollar_subscriptions_doesnt_match_dollar_topic() { - assert!(super::matches("sy$tem/metrics", "sy$tem/+")); - assert!(!super::matches("$system/metrics", "$system/+")); - assert!(!super::matches("$system/metrics", "+/+")); - } - - #[test] - fn topics_match_with_filters_as_expected() { - let topic = "a/b/c"; - let filter = "a/b/c"; - assert!(super::matches(topic, filter)); - - let topic = "a/b/c"; - let filter = "d/b/c"; - assert!(!super::matches(topic, filter)); - - let topic = "a/b/c"; - let filter = "a/b/e"; - assert!(!super::matches(topic, filter)); - - let topic = "a/b/c"; - let filter = "a/b/c/d"; - assert!(!super::matches(topic, filter)); - - let topic = "a/b/c"; - let filter = "#"; - assert!(super::matches(topic, filter)); - - let topic = "a/b/c"; - let filter = "a/b/c/#"; - assert!(super::matches(topic, filter)); - - let topic = "a/b/c/d"; - let filter = "a/b/c"; - assert!(!super::matches(topic, filter)); - - let topic = "a/b/c/d"; - let filter = "a/b/c/#"; - assert!(super::matches(topic, filter)); - - let topic = "a/b/c/d/e/f"; - let filter = "a/b/c/#"; - assert!(super::matches(topic, filter)); - - let topic = "a/b/c"; - let filter = "a/+/c"; - assert!(super::matches(topic, filter)); - let topic = "a/b/c/d/e"; - let filter = "a/+/c/+/e"; - assert!(super::matches(topic, filter)); - - let topic = "a/b"; - let filter = "a/b/+"; - assert!(!super::matches(topic, filter)); - - let filter1 = "a/b/+"; - let filter2 = "a/b/#"; - assert!(super::matches(filter1, filter2)); - assert!(!super::matches(filter2, filter1)); - - let filter1 = "a/b/+"; - let filter2 = "#"; - assert!(super::matches(filter1, filter2)); - - let filter1 = "a/+/c/d"; - let filter2 = "a/+/+/d"; - assert!(super::matches(filter1, filter2)); - assert!(!super::matches(filter2, filter1)); - } -} diff --git a/rumqttd-old/src/mqttbytes/v4/connack.rs b/rumqttd-old/src/mqttbytes/v4/connack.rs deleted file mode 100644 index 453919515..000000000 --- a/rumqttd-old/src/mqttbytes/v4/connack.rs +++ /dev/null @@ -1,124 +0,0 @@ -use super::*; -use bytes::{Buf, BufMut, Bytes, BytesMut}; - -/// Return code in connack -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(u8)] -pub enum ConnectReturnCode { - Success = 0, - RefusedProtocolVersion, - BadClientId, - ServiceUnavailable, - BadUserNamePassword, - NotAuthorized, -} - -/// Acknowledgement to connect packet -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ConnAck { - pub session_present: bool, - pub code: ConnectReturnCode, -} - -impl ConnAck { - pub fn new(code: ConnectReturnCode, session_present: bool) -> ConnAck { - ConnAck { - session_present, - code, - } - } - - fn len(&self) -> usize { - // sesssion present + code - - 1 + 1 - } - - pub fn read(fixed_header: FixedHeader, mut bytes: Bytes) -> Result { - let variable_header_index = fixed_header.fixed_header_len; - bytes.advance(variable_header_index); - - let flags = read_u8(&mut bytes)?; - let return_code = read_u8(&mut bytes)?; - - let session_present = (flags & 0x01) == 1; - let code = connect_return(return_code)?; - let connack = ConnAck { - session_present, - code, - }; - - Ok(connack) - } - - pub fn write(&self, buffer: &mut BytesMut) -> Result { - let len = self.len(); - buffer.put_u8(0x20); - - let count = write_remaining_length(buffer, len)?; - buffer.put_u8(self.session_present as u8); - buffer.put_u8(self.code as u8); - - Ok(1 + count + len) - } -} - -/// Connection return code type -fn connect_return(num: u8) -> Result { - match num { - 0 => Ok(ConnectReturnCode::Success), - 1 => Ok(ConnectReturnCode::RefusedProtocolVersion), - 2 => Ok(ConnectReturnCode::BadClientId), - 3 => Ok(ConnectReturnCode::ServiceUnavailable), - 4 => Ok(ConnectReturnCode::BadUserNamePassword), - 5 => Ok(ConnectReturnCode::NotAuthorized), - num => Err(Error::InvalidConnectReturnCode(num)), - } -} - -#[cfg(test)] -mod test { - use super::*; - use bytes::BytesMut; - use pretty_assertions::assert_eq; - - #[test] - fn connack_parsing_works() { - let mut stream = bytes::BytesMut::new(); - let packetstream = &[ - 0b0010_0000, - 0x02, // packet type, flags and remaining len - 0x01, - 0x00, // variable header. connack flags, connect return code - 0xDE, - 0xAD, - 0xBE, - 0xEF, // extra packets in the stream - ]; - - stream.extend_from_slice(&packetstream[..]); - let fixed_header = parse_fixed_header(stream.iter()).unwrap(); - let connack_bytes = stream.split_to(fixed_header.frame_length()).freeze(); - let connack = ConnAck::read(fixed_header, connack_bytes).unwrap(); - - assert_eq!( - connack, - ConnAck { - session_present: true, - code: ConnectReturnCode::Success, - } - ); - } - - #[test] - fn connack_encoding_works() { - let connack = ConnAck { - session_present: true, - code: ConnectReturnCode::Success, - }; - - let mut buf = BytesMut::new(); - connack.write(&mut buf).unwrap(); - assert_eq!(buf, vec![0b0010_0000, 0x02, 0x01, 0x00]); - } -} diff --git a/rumqttd-old/src/mqttbytes/v4/connect.rs b/rumqttd-old/src/mqttbytes/v4/connect.rs deleted file mode 100644 index 93c018651..000000000 --- a/rumqttd-old/src/mqttbytes/v4/connect.rs +++ /dev/null @@ -1,409 +0,0 @@ -use super::*; -use bytes::{Buf, Bytes}; - -/// Connection packet initiated by the client -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Connect { - /// Mqtt protocol version - pub protocol: Protocol, - /// Mqtt keep alive time - pub keep_alive: u16, - /// Client Id - pub client_id: String, - /// Clean session. Asks the broker to clear previous state - pub clean_session: bool, - /// Will that broker needs to publish when the client disconnects - pub last_will: Option, - /// Login credentials - pub login: Option, -} - -impl Connect { - pub fn new>(id: S) -> Connect { - Connect { - protocol: Protocol::V4, - keep_alive: 10, - client_id: id.into(), - clean_session: true, - last_will: None, - login: None, - } - } - - pub fn set_login, P: Into>(&mut self, u: U, p: P) -> &mut Connect { - let login = Login { - username: u.into(), - password: p.into(), - }; - - self.login = Some(login); - self - } - - pub fn len(&self) -> usize { - let mut len = 2 + "MQTT".len() // protocol name - + 1 // protocol version - + 1 // connect flags - + 2; // keep alive - - len += 2 + self.client_id.len(); - - // last will len - if let Some(last_will) = &self.last_will { - len += last_will.len(); - } - - // username and password len - if let Some(login) = &self.login { - len += login.len(); - } - - len - } - - pub fn read(fixed_header: FixedHeader, mut bytes: Bytes) -> Result { - let variable_header_index = fixed_header.fixed_header_len; - bytes.advance(variable_header_index); - - // Variable header - let protocol_name = read_mqtt_string(&mut bytes)?; - let protocol_level = read_u8(&mut bytes)?; - if protocol_name != "MQTT" { - return Err(Error::InvalidProtocol); - } - - let protocol = match protocol_level { - 4 => Protocol::V4, - 5 => Protocol::V5, - num => return Err(Error::InvalidProtocolLevel(num)), - }; - - let connect_flags = read_u8(&mut bytes)?; - let clean_session = (connect_flags & 0b10) != 0; - let keep_alive = read_u16(&mut bytes)?; - - let client_id = read_mqtt_string(&mut bytes)?; - let last_will = LastWill::read(connect_flags, &mut bytes)?; - let login = Login::read(connect_flags, &mut bytes)?; - - let connect = Connect { - protocol, - keep_alive, - client_id, - clean_session, - last_will, - login, - }; - - Ok(connect) - } - - pub fn write(&self, buffer: &mut BytesMut) -> Result { - let len = self.len(); - buffer.put_u8(0b0001_0000); - let count = write_remaining_length(buffer, len)?; - write_mqtt_string(buffer, "MQTT"); - - match self.protocol { - Protocol::V4 => buffer.put_u8(0x04), - Protocol::V5 => buffer.put_u8(0x05), - } - - let flags_index = 1 + count + 2 + 4 + 1; - - let mut connect_flags = 0; - if self.clean_session { - connect_flags |= 0x02; - } - - buffer.put_u8(connect_flags); - buffer.put_u16(self.keep_alive); - write_mqtt_string(buffer, &self.client_id); - - if let Some(last_will) = &self.last_will { - connect_flags |= last_will.write(buffer)?; - } - - if let Some(login) = &self.login { - connect_flags |= login.write(buffer); - } - - // update connect flags - buffer[flags_index] = connect_flags; - Ok(len) - } -} - -/// LastWill that broker forwards on behalf of the client -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct LastWill { - pub topic: String, - pub message: Bytes, - pub qos: QoS, - pub retain: bool, -} - -impl LastWill { - pub fn new( - topic: impl Into, - payload: impl Into>, - qos: QoS, - retain: bool, - ) -> LastWill { - LastWill { - topic: topic.into(), - message: Bytes::from(payload.into()), - qos, - retain, - } - } - - fn len(&self) -> usize { - let mut len = 0; - len += 2 + self.topic.len() + 2 + self.message.len(); - len - } - - fn read(connect_flags: u8, bytes: &mut Bytes) -> Result, Error> { - let last_will = match connect_flags & 0b100 { - 0 if (connect_flags & 0b0011_1000) != 0 => { - return Err(Error::IncorrectPacketFormat); - } - 0 => None, - _ => { - let will_topic = read_mqtt_string(bytes)?; - let will_message = read_mqtt_bytes(bytes)?; - let will_qos = qos((connect_flags & 0b11000) >> 3)?; - Some(LastWill { - topic: will_topic, - message: will_message, - qos: will_qos, - retain: (connect_flags & 0b0010_0000) != 0, - }) - } - }; - - Ok(last_will) - } - - fn write(&self, buffer: &mut BytesMut) -> Result { - let mut connect_flags = 0; - - connect_flags |= 0x04 | (self.qos as u8) << 3; - if self.retain { - connect_flags |= 0x20; - } - - write_mqtt_string(buffer, &self.topic); - write_mqtt_bytes(buffer, &self.message); - Ok(connect_flags) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Login { - pub username: String, - pub password: String, -} - -impl Login { - pub fn new, P: Into>(u: U, p: P) -> Login { - Login { - username: u.into(), - password: p.into(), - } - } - - fn read(connect_flags: u8, bytes: &mut Bytes) -> Result, Error> { - let username = match connect_flags & 0b1000_0000 { - 0 => String::new(), - _ => read_mqtt_string(bytes)?, - }; - - let password = match connect_flags & 0b0100_0000 { - 0 => String::new(), - _ => read_mqtt_string(bytes)?, - }; - - if username.is_empty() && password.is_empty() { - Ok(None) - } else { - Ok(Some(Login { username, password })) - } - } - - fn len(&self) -> usize { - let mut len = 0; - - if !self.username.is_empty() { - len += 2 + self.username.len(); - } - - if !self.password.is_empty() { - len += 2 + self.password.len(); - } - - len - } - - fn write(&self, buffer: &mut BytesMut) -> u8 { - let mut connect_flags = 0; - if !self.username.is_empty() { - connect_flags |= 0x80; - write_mqtt_string(buffer, &self.username); - } - - if !self.password.is_empty() { - connect_flags |= 0x40; - write_mqtt_string(buffer, &self.password); - } - - connect_flags - } - - pub fn validate(&self, username: &str, password: &str) -> bool { - (self.username == *username) && (self.password == *password) - } -} - -#[cfg(test)] -mod test { - use super::*; - use bytes::BytesMut; - use pretty_assertions::assert_eq; - - #[test] - fn connect_parsing_works() { - let mut stream = bytes::BytesMut::new(); - let packetstream = &[ - 0x10, - 39, // packet type, flags and remaining len - 0x00, - 0x04, - b'M', - b'Q', - b'T', - b'T', - 0x04, // variable header - 0b1100_1110, // variable header. +username, +password, -will retain, will qos=1, +last_will, +clean_session - 0x00, - 0x0a, // variable header. keep alive = 10 sec - 0x00, - 0x04, - b't', - b'e', - b's', - b't', // payload. client_id - 0x00, - 0x02, - b'/', - b'a', // payload. will topic = '/a' - 0x00, - 0x07, - b'o', - b'f', - b'f', - b'l', - b'i', - b'n', - b'e', // payload. variable header. will msg = 'offline' - 0x00, - 0x04, - b'r', - b'u', - b'm', - b'q', // payload. username = 'rumq' - 0x00, - 0x02, - b'm', - b'q', // payload. password = 'mq' - 0xDE, - 0xAD, - 0xBE, - 0xEF, // extra packets in the stream - ]; - - stream.extend_from_slice(&packetstream[..]); - let fixed_header = parse_fixed_header(stream.iter()).unwrap(); - let connect_bytes = stream.split_to(fixed_header.frame_length()).freeze(); - let packet = Connect::read(fixed_header, connect_bytes).unwrap(); - - assert_eq!( - packet, - Connect { - protocol: Protocol::V4, - keep_alive: 10, - client_id: "test".to_owned(), - clean_session: true, - last_will: Some(LastWill::new("/a", "offline", QoS::AtLeastOnce, false)), - login: Some(Login::new("rumq", "mq")), - } - ); - } - - fn sample_bytes() -> Vec { - vec![ - 0x10, - 39, - 0x00, - 0x04, - b'M', - b'Q', - b'T', - b'T', - 0x04, - 0b1100_1110, // +username, +password, -will retain, will qos=1, +last_will, +clean_session - 0x00, - 0x0a, // 10 sec - 0x00, - 0x04, - b't', - b'e', - b's', - b't', // client_id - 0x00, - 0x02, - b'/', - b'a', // will topic = '/a' - 0x00, - 0x07, - b'o', - b'f', - b'f', - b'l', - b'i', - b'n', - b'e', // will msg = 'offline' - 0x00, - 0x04, - b'r', - b'u', - b's', - b't', // username = 'rust' - 0x00, - 0x02, - b'm', - b'q', // password = 'mq' - ] - } - - #[test] - fn connect_encoding_works() { - let connect = Connect { - protocol: Protocol::V4, - keep_alive: 10, - client_id: "test".to_owned(), - clean_session: true, - last_will: Some(LastWill::new("/a", "offline", QoS::AtLeastOnce, false)), - login: Some(Login::new("rust", "mq")), - }; - - let mut buf = BytesMut::new(); - connect.write(&mut buf).unwrap(); - - // println!("{:?}", &buf[..]); - // println!("{:?}", sample_bytes()); - - assert_eq!(buf, sample_bytes()); - } -} diff --git a/rumqttd-old/src/mqttbytes/v4/mod.rs b/rumqttd-old/src/mqttbytes/v4/mod.rs deleted file mode 100644 index 58312b708..000000000 --- a/rumqttd-old/src/mqttbytes/v4/mod.rs +++ /dev/null @@ -1,85 +0,0 @@ -use super::*; - -mod connack; -mod connect; -mod ping; -mod puback; -mod pubcomp; -mod publish; -mod pubrec; -mod pubrel; -mod suback; -mod subscribe; -mod unsuback; -mod unsubscribe; - -pub use connack::*; -pub use connect::*; -pub use ping::*; -pub use puback::*; -pub use pubcomp::*; -pub use publish::*; -pub use pubrec::*; -pub use pubrel::*; -pub use suback::*; -pub use subscribe::*; -pub use unsuback::*; -pub use unsubscribe::*; - -/// Encapsulates all MQTT packet types -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum Packet { - Connect(Connect), - ConnAck(ConnAck), - Publish(Publish), - PubAck(PubAck), - PubRec(PubRec), - PubRel(PubRel), - PubComp(PubComp), - Subscribe(Subscribe), - SubAck(SubAck), - Unsubscribe(Unsubscribe), - UnsubAck(UnsubAck), - PingReq, - PingResp, - Disconnect, -} - -/// Reads a stream of bytes and extracts next MQTT packet out of it -pub fn read(stream: &mut BytesMut, max_size: usize) -> Result { - let fixed_header = check(stream.iter(), max_size)?; - - // Test with a stream with exactly the size to check border panics - let packet = stream.split_to(fixed_header.frame_length()); - let packet_type = fixed_header.packet_type()?; - - if fixed_header.remaining_len == 0 { - // no payload packets - return match packet_type { - PacketType::PingReq => Ok(Packet::PingReq), - PacketType::PingResp => Ok(Packet::PingResp), - PacketType::Disconnect => Ok(Packet::Disconnect), - _ => Err(Error::PayloadRequired), - }; - } - - let packet = packet.freeze(); - let packet = match packet_type { - PacketType::Connect => Packet::Connect(Connect::read(fixed_header, packet)?), - PacketType::ConnAck => Packet::ConnAck(ConnAck::read(fixed_header, packet)?), - PacketType::Publish => Packet::Publish(Publish::read(fixed_header, packet)?), - PacketType::PubAck => Packet::PubAck(PubAck::read(fixed_header, packet)?), - PacketType::PubRec => Packet::PubRec(PubRec::read(fixed_header, packet)?), - PacketType::PubRel => Packet::PubRel(PubRel::read(fixed_header, packet)?), - PacketType::PubComp => Packet::PubComp(PubComp::read(fixed_header, packet)?), - PacketType::Subscribe => Packet::Subscribe(Subscribe::read(fixed_header, packet)?), - PacketType::SubAck => Packet::SubAck(SubAck::read(fixed_header, packet)?), - PacketType::Unsubscribe => Packet::Unsubscribe(Unsubscribe::read(fixed_header, packet)?), - PacketType::UnsubAck => Packet::UnsubAck(UnsubAck::read(fixed_header, packet)?), - PacketType::PingReq => Packet::PingReq, - PacketType::PingResp => Packet::PingResp, - PacketType::Disconnect => Packet::Disconnect, - }; - - Ok(packet) -} diff --git a/rumqttd-old/src/mqttbytes/v4/ping.rs b/rumqttd-old/src/mqttbytes/v4/ping.rs deleted file mode 100644 index 89554fa28..000000000 --- a/rumqttd-old/src/mqttbytes/v4/ping.rs +++ /dev/null @@ -1,11 +0,0 @@ -use super::*; -use bytes::{BufMut, BytesMut}; - -pub struct PingResp; - -impl PingResp { - pub fn write(&self, payload: &mut BytesMut) -> Result { - payload.put_slice(&[0xD0, 0x00]); - Ok(2) - } -} diff --git a/rumqttd-old/src/mqttbytes/v4/puback.rs b/rumqttd-old/src/mqttbytes/v4/puback.rs deleted file mode 100644 index c878b7eb3..000000000 --- a/rumqttd-old/src/mqttbytes/v4/puback.rs +++ /dev/null @@ -1,74 +0,0 @@ -use super::*; -use bytes::{Buf, BufMut, Bytes, BytesMut}; - -/// Acknowledgement to QoS1 publish -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct PubAck { - pub pkid: u16, -} - -impl PubAck { - pub fn new(pkid: u16) -> PubAck { - PubAck { pkid } - } - - fn len(&self) -> usize { - // pkid - 2 - } - - pub fn read(fixed_header: FixedHeader, mut bytes: Bytes) -> Result { - let variable_header_index = fixed_header.fixed_header_len; - bytes.advance(variable_header_index); - let pkid = read_u16(&mut bytes)?; - - // No reason code or properties if remaining length == 2 - if fixed_header.remaining_len == 2 { - return Ok(PubAck { pkid }); - } - - // No properties len or properties if remaining len > 2 but < 4 - if fixed_header.remaining_len < 4 { - return Ok(PubAck { pkid }); - } - - let puback = PubAck { pkid }; - - Ok(puback) - } - - pub fn write(&self, buffer: &mut BytesMut) -> Result { - let len = self.len(); - buffer.put_u8(0x40); - let count = write_remaining_length(buffer, len)?; - buffer.put_u16(self.pkid); - Ok(1 + count + len) - } -} - -#[cfg(test)] -mod test { - use super::*; - use bytes::BytesMut; - use pretty_assertions::assert_eq; - - #[test] - fn puback_encoding_works() { - let stream = &[ - 0b0100_0000, - 0x02, // packet type, flags and remaining len - 0x00, - 0x0A, // fixed header. packet identifier = 10 - 0xDE, - 0xAD, - 0xBE, - 0xEF, // extra packets in the stream - ]; - let mut stream = BytesMut::from(&stream[..]); - let fixed_header = parse_fixed_header(stream.iter()).unwrap(); - let ack_bytes = stream.split_to(fixed_header.frame_length()).freeze(); - let packet = PubAck::read(fixed_header, ack_bytes).unwrap(); - - assert_eq!(packet, PubAck { pkid: 10 }); - } -} diff --git a/rumqttd-old/src/mqttbytes/v4/pubcomp.rs b/rumqttd-old/src/mqttbytes/v4/pubcomp.rs deleted file mode 100644 index c78567004..000000000 --- a/rumqttd-old/src/mqttbytes/v4/pubcomp.rs +++ /dev/null @@ -1,45 +0,0 @@ -use super::*; -use bytes::{Buf, BufMut, Bytes, BytesMut}; - -/// QoS2 Assured publish complete, in response to PUBREL packet -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct PubComp { - pub pkid: u16, -} - -impl PubComp { - pub fn new(pkid: u16) -> PubComp { - PubComp { pkid } - } - - fn len(&self) -> usize { - // pkid - 2 - } - - pub fn read(fixed_header: FixedHeader, mut bytes: Bytes) -> Result { - let variable_header_index = fixed_header.fixed_header_len; - bytes.advance(variable_header_index); - let pkid = read_u16(&mut bytes)?; - - if fixed_header.remaining_len == 2 { - return Ok(PubComp { pkid }); - } - - if fixed_header.remaining_len < 4 { - return Ok(PubComp { pkid }); - } - - let puback = PubComp { pkid }; - - Ok(puback) - } - - pub fn write(&self, buffer: &mut BytesMut) -> Result { - let len = self.len(); - buffer.put_u8(0x70); - let count = write_remaining_length(buffer, len)?; - buffer.put_u16(self.pkid); - Ok(1 + count + len) - } -} diff --git a/rumqttd-old/src/mqttbytes/v4/publish.rs b/rumqttd-old/src/mqttbytes/v4/publish.rs deleted file mode 100644 index 285d0a706..000000000 --- a/rumqttd-old/src/mqttbytes/v4/publish.rs +++ /dev/null @@ -1,268 +0,0 @@ -use super::*; -use bytes::{Buf, Bytes}; - -/// Publish packet -#[derive(Clone, PartialEq, Eq)] -pub struct Publish { - pub dup: bool, - pub qos: QoS, - pub retain: bool, - pub topic: String, - pub pkid: u16, - pub payload: Bytes, -} - -impl Publish { - pub fn new, P: Into>>(topic: S, qos: QoS, payload: P) -> Publish { - Publish { - dup: false, - qos, - retain: false, - pkid: 0, - topic: topic.into(), - payload: Bytes::from(payload.into()), - } - } - - pub fn from_bytes>(topic: S, qos: QoS, payload: Bytes) -> Publish { - Publish { - dup: false, - qos, - retain: false, - pkid: 0, - topic: topic.into(), - payload, - } - } - - pub fn len(&self) -> usize { - let mut len = 2 + self.topic.len(); - if self.qos != QoS::AtMostOnce && self.pkid != 0 { - len += 2; - } - - len += self.payload.len(); - len - } - - pub fn read(fixed_header: FixedHeader, mut bytes: Bytes) -> Result { - let qos = qos((fixed_header.byte1 & 0b0110) >> 1)?; - let dup = (fixed_header.byte1 & 0b1000) != 0; - let retain = (fixed_header.byte1 & 0b0001) != 0; - - let variable_header_index = fixed_header.fixed_header_len; - bytes.advance(variable_header_index); - let topic = read_mqtt_string(&mut bytes)?; - - // Packet identifier exists where QoS > 0 - let pkid = match qos { - QoS::AtMostOnce => 0, - QoS::AtLeastOnce | QoS::ExactlyOnce => read_u16(&mut bytes)?, - }; - - if qos != QoS::AtMostOnce && pkid == 0 { - return Err(Error::PacketIdZero); - } - - let publish = Publish { - dup, - retain, - qos, - pkid, - topic, - payload: bytes, - }; - - Ok(publish) - } - - pub fn write(&self, buffer: &mut BytesMut) -> Result { - let len = self.len(); - - let dup = self.dup as u8; - let qos = self.qos as u8; - let retain = self.retain as u8; - buffer.put_u8(0b0011_0000 | retain | qos << 1 | dup << 3); - - let count = write_remaining_length(buffer, len)?; - write_mqtt_string(buffer, self.topic.as_str()); - - if self.qos != QoS::AtMostOnce { - let pkid = self.pkid; - if pkid == 0 { - return Err(Error::PacketIdZero); - } - - buffer.put_u16(pkid); - } - - buffer.extend_from_slice(&self.payload); - - // TODO: Returned length is wrong in other packets. Fix it - Ok(1 + count + len) - } -} - -impl fmt::Debug for Publish { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "Topic = {}, Qos = {:?}, Retain = {}, Pkid = {:?}, Payload Size = {}", - self.topic, - self.qos, - self.retain, - self.pkid, - self.payload.len() - ) - } -} - -#[cfg(test)] -mod test { - use super::*; - use bytes::{Bytes, BytesMut}; - use pretty_assertions::assert_eq; - - #[test] - fn qos1_publish_parsing_works() { - let stream = &[ - 0b0011_0010, - 11, // packet type, flags and remaining len - 0x00, - 0x03, - b'a', - b'/', - b'b', // variable header. topic name = 'a/b' - 0x00, - 0x0a, // variable header. pkid = 10 - 0xF1, - 0xF2, - 0xF3, - 0xF4, // publish payload - 0xDE, - 0xAD, - 0xBE, - 0xEF, // extra packets in the stream - ]; - - let mut stream = BytesMut::from(&stream[..]); - let fixed_header = parse_fixed_header(stream.iter()).unwrap(); - let publish_bytes = stream.split_to(fixed_header.frame_length()).freeze(); - let packet = Publish::read(fixed_header, publish_bytes).unwrap(); - - let payload = &[0xF1, 0xF2, 0xF3, 0xF4]; - assert_eq!( - packet, - Publish { - dup: false, - qos: QoS::AtLeastOnce, - retain: false, - topic: "a/b".to_owned(), - pkid: 10, - payload: Bytes::from(&payload[..]), - } - ); - } - - #[test] - fn qos0_publish_parsing_works() { - let stream = &[ - 0b0011_0000, - 7, // packet type, flags and remaining len - 0x00, - 0x03, - b'a', - b'/', - b'b', // variable header. topic name = 'a/b' - 0x01, - 0x02, // payload - 0xDE, - 0xAD, - 0xBE, - 0xEF, // extra packets in the stream - ]; - - let mut stream = BytesMut::from(&stream[..]); - let fixed_header = parse_fixed_header(stream.iter()).unwrap(); - let publish_bytes = stream.split_to(fixed_header.frame_length()).freeze(); - let packet = Publish::read(fixed_header, publish_bytes).unwrap(); - - assert_eq!( - packet, - Publish { - dup: false, - qos: QoS::AtMostOnce, - retain: false, - topic: "a/b".to_owned(), - pkid: 0, - payload: Bytes::from(&[0x01, 0x02][..]), - } - ); - } - - #[test] - fn qos1_publish_encoding_works() { - let publish = Publish { - dup: false, - qos: QoS::AtLeastOnce, - retain: false, - topic: "a/b".to_owned(), - pkid: 10, - payload: Bytes::from(vec![0xF1, 0xF2, 0xF3, 0xF4]), - }; - - let mut buf = BytesMut::new(); - publish.write(&mut buf).unwrap(); - - assert_eq!( - buf, - vec![ - 0b0011_0010, - 11, - 0x00, - 0x03, - b'a', - b'/', - b'b', - 0x00, - 0x0a, - 0xF1, - 0xF2, - 0xF3, - 0xF4 - ] - ); - } - - #[test] - fn qos0_publish_encoding_works() { - let publish = Publish { - dup: false, - qos: QoS::AtMostOnce, - retain: false, - topic: "a/b".to_owned(), - pkid: 0, - payload: Bytes::from(vec![0xE1, 0xE2, 0xE3, 0xE4]), - }; - - let mut buf = BytesMut::new(); - publish.write(&mut buf).unwrap(); - - assert_eq!( - buf, - vec![ - 0b0011_0000, - 9, - 0x00, - 0x03, - b'a', - b'/', - b'b', - 0xE1, - 0xE2, - 0xE3, - 0xE4 - ] - ); - } -} diff --git a/rumqttd-old/src/mqttbytes/v4/pubrec.rs b/rumqttd-old/src/mqttbytes/v4/pubrec.rs deleted file mode 100644 index 197442b39..000000000 --- a/rumqttd-old/src/mqttbytes/v4/pubrec.rs +++ /dev/null @@ -1,44 +0,0 @@ -use super::*; -use bytes::{Buf, BufMut, Bytes, BytesMut}; - -/// Acknowledgement to QoS2 publish -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct PubRec { - pub pkid: u16, -} - -impl PubRec { - pub fn new(pkid: u16) -> PubRec { - PubRec { pkid } - } - - fn len(&self) -> usize { - // pkid - 2 - } - - pub fn read(fixed_header: FixedHeader, mut bytes: Bytes) -> Result { - let variable_header_index = fixed_header.fixed_header_len; - bytes.advance(variable_header_index); - let pkid = read_u16(&mut bytes)?; - if fixed_header.remaining_len == 2 { - return Ok(PubRec { pkid }); - } - - if fixed_header.remaining_len < 4 { - return Ok(PubRec { pkid }); - } - - let puback = PubRec { pkid }; - - Ok(puback) - } - - pub fn write(&self, buffer: &mut BytesMut) -> Result { - let len = self.len(); - buffer.put_u8(0x50); - let count = write_remaining_length(buffer, len)?; - buffer.put_u16(self.pkid); - Ok(1 + count + len) - } -} diff --git a/rumqttd-old/src/mqttbytes/v4/pubrel.rs b/rumqttd-old/src/mqttbytes/v4/pubrel.rs deleted file mode 100644 index ce2e3aa42..000000000 --- a/rumqttd-old/src/mqttbytes/v4/pubrel.rs +++ /dev/null @@ -1,44 +0,0 @@ -use super::*; -use bytes::{Buf, BufMut, Bytes, BytesMut}; - -/// QoS2 Publish release, in response to PUBREC packet -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct PubRel { - pub pkid: u16, -} - -impl PubRel { - pub fn new(pkid: u16) -> PubRel { - PubRel { pkid } - } - - fn len(&self) -> usize { - // pkid - 2 - } - - pub fn read(fixed_header: FixedHeader, mut bytes: Bytes) -> Result { - let variable_header_index = fixed_header.fixed_header_len; - bytes.advance(variable_header_index); - let pkid = read_u16(&mut bytes)?; - if fixed_header.remaining_len == 2 { - return Ok(PubRel { pkid }); - } - - if fixed_header.remaining_len < 4 { - return Ok(PubRel { pkid }); - } - - let puback = PubRel { pkid }; - - Ok(puback) - } - - pub fn write(&self, buffer: &mut BytesMut) -> Result { - let len = self.len(); - buffer.put_u8(0x62); - let count = write_remaining_length(buffer, len)?; - buffer.put_u16(self.pkid); - Ok(1 + count + len) - } -} diff --git a/rumqttd-old/src/mqttbytes/v4/suback.rs b/rumqttd-old/src/mqttbytes/v4/suback.rs deleted file mode 100644 index bf970c310..000000000 --- a/rumqttd-old/src/mqttbytes/v4/suback.rs +++ /dev/null @@ -1,112 +0,0 @@ -use super::*; -use bytes::{Buf, BufMut, Bytes, BytesMut}; -use std::convert::{TryFrom, TryInto}; - -/// Acknowledgement to subscribe -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct SubAck { - pub pkid: u16, - pub return_codes: Vec, -} - -impl SubAck { - pub fn new(pkid: u16, return_codes: Vec) -> SubAck { - SubAck { pkid, return_codes } - } - - pub fn len(&self) -> usize { - 2 + self.return_codes.len() - } - - pub fn read(fixed_header: FixedHeader, mut bytes: Bytes) -> Result { - let variable_header_index = fixed_header.fixed_header_len; - bytes.advance(variable_header_index); - let pkid = read_u16(&mut bytes)?; - - if !bytes.has_remaining() { - return Err(Error::MalformedPacket); - } - - let mut return_codes = Vec::new(); - while bytes.has_remaining() { - let return_code = read_u8(&mut bytes)?; - return_codes.push(return_code.try_into()?); - } - - let suback = SubAck { pkid, return_codes }; - Ok(suback) - } - - pub fn write(&self, buffer: &mut BytesMut) -> Result { - buffer.put_u8(0x90); - let remaining_len = self.len(); - let remaining_len_bytes = write_remaining_length(buffer, remaining_len)?; - - buffer.put_u16(self.pkid); - let p: Vec = self - .return_codes - .iter() - .map(|&code| match code { - SubscribeReasonCode::Success(qos) => qos as u8, - SubscribeReasonCode::Failure => 0x80, - }) - .collect(); - buffer.extend_from_slice(&p); - Ok(1 + remaining_len_bytes + remaining_len) - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum SubscribeReasonCode { - Success(QoS), - Failure, -} - -impl TryFrom for SubscribeReasonCode { - type Error = super::Error; - - fn try_from(value: u8) -> Result { - let v = match value { - 0 => SubscribeReasonCode::Success(QoS::AtMostOnce), - 1 => SubscribeReasonCode::Success(QoS::AtLeastOnce), - 2 => SubscribeReasonCode::Success(QoS::ExactlyOnce), - 128 => SubscribeReasonCode::Failure, - v => return Err(super::Error::InvalidSubscribeReasonCode(v)), - }; - - Ok(v) - } -} - -#[cfg(test)] -mod test { - use super::*; - use bytes::BytesMut; - use pretty_assertions::assert_eq; - - #[test] - fn suback_parsing_works() { - let stream = vec![ - 0x90, 4, // packet type, flags and remaining len - 0x00, 0x0F, // variable header. pkid = 15 - 0x01, 0x80, // payload. return codes [success qos1, failure] - 0xDE, 0xAD, 0xBE, 0xEF, // extra packets in the stream - ]; - - let mut stream = BytesMut::from(&stream[..]); - let fixed_header = parse_fixed_header(stream.iter()).unwrap(); - let ack_bytes = stream.split_to(fixed_header.frame_length()).freeze(); - let packet = SubAck::read(fixed_header, ack_bytes).unwrap(); - - assert_eq!( - packet, - SubAck { - pkid: 15, - return_codes: vec![ - SubscribeReasonCode::Success(QoS::AtLeastOnce), - SubscribeReasonCode::Failure, - ], - } - ); - } -} diff --git a/rumqttd-old/src/mqttbytes/v4/subscribe.rs b/rumqttd-old/src/mqttbytes/v4/subscribe.rs deleted file mode 100644 index df874e501..000000000 --- a/rumqttd-old/src/mqttbytes/v4/subscribe.rs +++ /dev/null @@ -1,235 +0,0 @@ -use super::*; -use bytes::{Buf, Bytes}; - -/// Subscription packet -#[derive(Clone, PartialEq, Eq)] -pub struct Subscribe { - pub pkid: u16, - pub filters: Vec, -} - -impl Subscribe { - pub fn new>(path: S, qos: QoS) -> Subscribe { - let filter = SubscribeFilter { - path: path.into(), - qos, - }; - - Subscribe { - pkid: 0, - filters: vec![filter], - } - } - - pub fn new_many(topics: T) -> Subscribe - where - T: IntoIterator, - { - Subscribe { - pkid: 0, - filters: topics.into_iter().collect(), - } - } - - pub fn empty_subscribe() -> Subscribe { - Subscribe { - pkid: 0, - filters: Vec::new(), - } - } - - pub fn add(&mut self, path: String, qos: QoS) -> &mut Self { - let filter = SubscribeFilter { path, qos }; - - self.filters.push(filter); - self - } - - pub fn len(&self) -> usize { - let len = 2 + self.filters.iter().fold(0, |s, t| s + t.len()); // len of pkid + vec![subscribe filter len] - len - } - - pub fn read(fixed_header: FixedHeader, mut bytes: Bytes) -> Result { - let variable_header_index = fixed_header.fixed_header_len; - bytes.advance(variable_header_index); - - let pkid = read_u16(&mut bytes)?; - - // variable header size = 2 (packet identifier) - let mut filters = Vec::new(); - - while bytes.has_remaining() { - let path = read_mqtt_string(&mut bytes)?; - let options = read_u8(&mut bytes)?; - let requested_qos = options & 0b0000_0011; - - filters.push(SubscribeFilter { - path, - qos: qos(requested_qos)?, - }); - } - - let subscribe = Subscribe { pkid, filters }; - - Ok(subscribe) - } - - pub fn write(&self, buffer: &mut BytesMut) -> Result { - // write packet type - buffer.put_u8(0x82); - - // write remaining length - let remaining_len = self.len(); - let remaining_len_bytes = write_remaining_length(buffer, remaining_len)?; - - // write packet id - buffer.put_u16(self.pkid); - - // write filters - for filter in self.filters.iter() { - filter.write(buffer); - } - - Ok(1 + remaining_len_bytes + remaining_len) - } -} - -/// Subscription filter -#[derive(Clone, PartialEq, Eq)] -pub struct SubscribeFilter { - pub path: String, - pub qos: QoS, -} - -impl SubscribeFilter { - pub fn new(path: String, qos: QoS) -> SubscribeFilter { - SubscribeFilter { path, qos } - } - - pub fn len(&self) -> usize { - // filter len + filter + options - 2 + self.path.len() + 1 - } - - fn write(&self, buffer: &mut BytesMut) { - let mut options = 0; - options |= self.qos as u8; - - write_mqtt_string(buffer, self.path.as_str()); - buffer.put_u8(options); - } -} - -impl fmt::Debug for Subscribe { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "Filters = {:?}, Packet id = {:?}", - self.filters, self.pkid - ) - } -} - -impl fmt::Debug for SubscribeFilter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Filter = {}, Qos = {:?}", self.path, self.qos) - } -} - -#[cfg(test)] -mod test { - use super::*; - use bytes::BytesMut; - use pretty_assertions::assert_eq; - - #[test] - fn subscribe_parsing_works() { - let stream = &[ - 0b1000_0010, - 20, // packet type, flags and remaining len - 0x01, - 0x04, // variable header. pkid = 260 - 0x00, - 0x03, - b'a', - b'/', - b'+', // payload. topic filter = 'a/+' - 0x00, // payload. qos = 0 - 0x00, - 0x01, - b'#', // payload. topic filter = '#' - 0x01, // payload. qos = 1 - 0x00, - 0x05, - b'a', - b'/', - b'b', - b'/', - b'c', // payload. topic filter = 'a/b/c' - 0x02, // payload. qos = 2 - 0xDE, - 0xAD, - 0xBE, - 0xEF, // extra packets in the stream - ]; - let mut stream = BytesMut::from(&stream[..]); - let fixed_header = parse_fixed_header(stream.iter()).unwrap(); - let subscribe_bytes = stream.split_to(fixed_header.frame_length()).freeze(); - let packet = Subscribe::read(fixed_header, subscribe_bytes).unwrap(); - - assert_eq!( - packet, - Subscribe { - pkid: 260, - filters: vec![ - SubscribeFilter::new("a/+".to_owned(), QoS::AtMostOnce), - SubscribeFilter::new("#".to_owned(), QoS::AtLeastOnce), - SubscribeFilter::new("a/b/c".to_owned(), QoS::ExactlyOnce) - ], - } - ); - } - - #[test] - fn subscribe_encoding_works() { - let subscribe = Subscribe { - pkid: 260, - filters: vec![ - SubscribeFilter::new("a/+".to_owned(), QoS::AtMostOnce), - SubscribeFilter::new("#".to_owned(), QoS::AtLeastOnce), - SubscribeFilter::new("a/b/c".to_owned(), QoS::ExactlyOnce), - ], - }; - - let mut buf = BytesMut::new(); - subscribe.write(&mut buf).unwrap(); - assert_eq!( - buf, - vec![ - 0b1000_0010, - 20, - 0x01, - 0x04, // pkid = 260 - 0x00, - 0x03, - b'a', - b'/', - b'+', // topic filter = 'a/+' - 0x00, // qos = 0 - 0x00, - 0x01, - b'#', // topic filter = '#' - 0x01, // qos = 1 - 0x00, - 0x05, - b'a', - b'/', - b'b', - b'/', - b'c', // topic filter = 'a/b/c' - 0x02 // qos = 2 - ] - ); - } -} diff --git a/rumqttd-old/src/mqttbytes/v4/unsuback.rs b/rumqttd-old/src/mqttbytes/v4/unsuback.rs deleted file mode 100644 index 36c6abe8a..000000000 --- a/rumqttd-old/src/mqttbytes/v4/unsuback.rs +++ /dev/null @@ -1,34 +0,0 @@ -use bytes::{Buf, BufMut, Bytes, BytesMut}; - -use crate::mqttbytes::{read_u16, Error, FixedHeader}; - -/// Acknowledgement to unsubscribe -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct UnsubAck { - pub pkid: u16, -} - -impl UnsubAck { - pub fn new(pkid: u16) -> UnsubAck { - UnsubAck { pkid } - } - - pub fn read(fixed_header: FixedHeader, mut bytes: Bytes) -> Result { - if fixed_header.remaining_len != 2 { - return Err(Error::PayloadSizeIncorrect); - } - - let variable_header_index = fixed_header.fixed_header_len; - bytes.advance(variable_header_index); - let pkid = read_u16(&mut bytes)?; - let unsuback = UnsubAck { pkid }; - - Ok(unsuback) - } - - pub fn write(&self, payload: &mut BytesMut) -> Result { - payload.put_slice(&[0xB0, 0x02]); - payload.put_u16(self.pkid); - Ok(4) - } -} diff --git a/rumqttd-old/src/mqttbytes/v4/unsubscribe.rs b/rumqttd-old/src/mqttbytes/v4/unsubscribe.rs deleted file mode 100644 index 2c9f0529b..000000000 --- a/rumqttd-old/src/mqttbytes/v4/unsubscribe.rs +++ /dev/null @@ -1,49 +0,0 @@ -use super::*; -use bytes::{Buf, Bytes}; - -/// Unsubscribe packet -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Unsubscribe { - pub pkid: u16, - pub topics: Vec, -} - -impl Unsubscribe { - pub fn new>(topic: S) -> Unsubscribe { - Unsubscribe { - pkid: 0, - topics: vec![topic.into()], - } - } - - pub fn read(fixed_header: FixedHeader, mut bytes: Bytes) -> Result { - let variable_header_index = fixed_header.fixed_header_len; - bytes.advance(variable_header_index); - - let pkid = read_u16(&mut bytes)?; - let mut payload_bytes = fixed_header.remaining_len - 2; - let mut topics = Vec::with_capacity(1); - - while payload_bytes > 0 { - let topic_filter = read_mqtt_string(&mut bytes)?; - payload_bytes -= topic_filter.len() + 2; - topics.push(topic_filter); - } - - let unsubscribe = Unsubscribe { pkid, topics }; - Ok(unsubscribe) - } - - pub fn write(&self, payload: &mut BytesMut) -> Result { - let remaining_len = 2 + self.topics.iter().fold(0, |s, topic| s + topic.len() + 2); - - payload.put_u8(0xA2); - let remaining_len_bytes = write_remaining_length(payload, remaining_len)?; - payload.put_u16(self.pkid); - - for topic in self.topics.iter() { - write_mqtt_string(payload, topic.as_str()); - } - Ok(1 + remaining_len_bytes + remaining_len) - } -} diff --git a/rumqttd-old/src/network.rs b/rumqttd-old/src/network.rs deleted file mode 100644 index 3b56f9fa3..000000000 --- a/rumqttd-old/src/network.rs +++ /dev/null @@ -1,195 +0,0 @@ -use crate::mqttbytes::{self, v4::*}; -use bytes::BytesMut; -use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; - -use crate::state; -use crate::state::State; -use std::io::{self, ErrorKind}; -use tokio::time::{self, error::Elapsed, Duration}; - -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("I/O: {0}")] - Io(#[from] io::Error), - #[error("State: {0}")] - State(#[from] state::Error), - #[error("Invalid data: {0}")] - Mqtt(#[from] mqttbytes::Error), - #[error("Keep alive timeout")] - KeepAlive(#[from] Elapsed), -} - -/// Network transforms packets <-> frames efficiently. It takes -/// advantage of pre-allocation, buffering and vectorization when -/// appropriate to achieve performance -pub struct Network { - /// Socket for IO - socket: Box, - /// Buffered reads - read: BytesMut, - /// Maximum packet size - max_incoming_size: usize, - /// Maximum readv count - max_readb_count: usize, - keepalive: Duration, -} - -impl Network { - pub fn new(socket: impl N + 'static, max_incoming_size: usize) -> Network { - let socket = Box::new(socket) as Box; - Network { - socket, - read: BytesMut::with_capacity(10 * 1024), - max_incoming_size, - max_readb_count: 10, - keepalive: Duration::from_secs(0), - } - } - - pub fn set_keepalive(&mut self, keepalive: u16) { - let keepalive = Duration::from_secs(keepalive as u64); - self.keepalive = keepalive + keepalive.mul_f32(0.5); - } - - /// Reads more than 'required' bytes to frame a packet into self.read buffer - async fn read_bytes(&mut self, required: usize) -> io::Result { - let mut total_read = 0; - loop { - let read = self.socket.read_buf(&mut self.read).await?; - if 0 == read { - return if self.read.is_empty() { - Err(io::Error::new( - ErrorKind::ConnectionAborted, - "connection closed by peer", - )) - } else { - Err(io::Error::new( - ErrorKind::ConnectionReset, - "connection reset by peer", - )) - }; - } - - total_read += read; - if total_read >= required { - return Ok(total_read); - } - } - } - - pub async fn read(&mut self) -> io::Result { - loop { - let required = match read(&mut self.read, self.max_incoming_size) { - Ok(packet) => return Ok(packet), - Err(mqttbytes::Error::InsufficientBytes(required)) => required, - Err(e) => return Err(io::Error::new(ErrorKind::InvalidData, e.to_string())), - }; - - // read more packets until a frame can be created. This function - // blocks until a frame can be created. Use this in a select! branch - self.read_bytes(required).await?; - } - } - - pub async fn read_connect(&mut self) -> io::Result { - let packet = self.read().await?; - - match packet { - Packet::Connect(connect) => Ok(connect), - packet => { - let error = format!("Expecting connect. Received = {:?}", packet); - Err(io::Error::new(io::ErrorKind::InvalidData, error)) - } - } - } - - pub async fn _read_connack(&mut self) -> io::Result { - let packet = self.read().await?; - - match packet { - Packet::ConnAck(connack) => Ok(connack), - packet => { - let error = format!("Expecting connack. Received = {:?}", packet); - Err(io::Error::new(io::ErrorKind::InvalidData, error)) - } - } - } - - pub async fn readb(&mut self, state: &mut State) -> Result { - if self.keepalive.as_secs() > 0 { - let disconnect = time::timeout(self.keepalive, async { - let disconnect = self.collect(state).await?; - Ok::<_, Error>(disconnect) - }) - .await??; - - Ok(disconnect) - } else { - let disconnect = self.collect(state).await?; - Ok(disconnect) - } - } - - /// Read packets in bulk. This allow replies to be in bulk. This method is used - /// after the connection is established to read a bunch of incoming packets - pub async fn collect(&mut self, state: &mut State) -> Result { - let mut count = 0; - let mut disconnect = false; - - loop { - match read(&mut self.read, self.max_incoming_size) { - // Store packet and return after enough packets are accumulated - Ok(packet) => { - disconnect = state.handle_network_data(packet)?; - count += 1; - - if count >= self.max_readb_count { - return Ok(disconnect); - } - } - // If some packets are already framed, return those - Err(mqttbytes::Error::InsufficientBytes(_)) if count > 0 => return Ok(disconnect), - // Wait for more bytes until a frame can be created - Err(mqttbytes::Error::InsufficientBytes(required)) => { - self.read_bytes(required).await?; - } - Err(e) => return Err(Error::Mqtt(e)), - }; - } - } - - pub async fn _connect(&mut self, connect: Connect) -> io::Result { - let mut write = BytesMut::new(); - let len = match connect.write(&mut write) { - Ok(size) => size, - Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidData, e.to_string())), - }; - - self.socket.write_all(&write[..]).await?; - Ok(len) - } - - pub async fn connack(&mut self, connack: ConnAck) -> io::Result { - let mut write = BytesMut::new(); - let len = match connack.write(&mut write) { - Ok(size) => size, - Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidData, e.to_string())), - }; - - self.socket.write_all(&write[..]).await?; - Ok(len) - } - - pub async fn flush(&mut self, write: &mut BytesMut) -> io::Result<()> { - if write.is_empty() { - return Ok(()); - } - - self.socket.write_all(&write[..]).await?; - write.clear(); - Ok(()) - } -} - -pub trait N: AsyncRead + AsyncWrite + Send + Sync + Unpin {} -impl N for T where T: AsyncRead + AsyncWrite + Unpin + Send + Sync {} diff --git a/rumqttd-old/src/remotelink.rs b/rumqttd-old/src/remotelink.rs deleted file mode 100644 index e823f3c5d..000000000 --- a/rumqttd-old/src/remotelink.rs +++ /dev/null @@ -1,288 +0,0 @@ -use crate::mqttbytes::v4::*; -use crate::mqttbytes::*; -use crate::network::Network; -use crate::rumqttlog::{ - Connection, ConnectionAck, Event, Notification, Receiver, RecvError, SendError, Sender, -}; -use crate::state::{self, State}; -use crate::{network, ConnectionSettings, Id}; - -use log::{debug, trace, warn}; -use std::sync::Arc; -use std::{io, mem}; -use tokio::time::{error::Elapsed, Duration}; -use tokio::{select, time}; - -pub struct RemoteLink { - config: Arc, - connect: Connect, - id: Id, - network: Network, - router_tx: Sender<(Id, Event)>, - link_rx: Receiver, - total: usize, - pending: Vec, - pub(crate) state: State, -} - -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("I/O: {0}")] - Io(#[from] io::Error), - #[error("Network: {0}")] - Network(#[from] network::Error), - #[error("Timeout")] - Timeout(#[from] Elapsed), - #[error("State error: {0}")] - State(#[from] state::Error), - #[error("Unexpected router message: {0:?}")] - RouterMessage(Notification), - #[error("Connack error: {0}")] - ConnAck(String), - #[error("Keep alive time exceeded")] - KeepAlive, - #[error("Channel send error")] - Send(#[from] SendError<(Id, Event)>), - #[error("Channel recv error")] - Recv(#[from] RecvError), - #[error("Payload count ({0}) greater than max inflight")] - TooManyPayloads(usize), - #[error("Persistent session requires valid client id")] - InvalidClientId, - #[error("Invalid username or password provided")] - InvalidUsernameOrPassword, - #[error("Disconnect request")] - Disconnect, -} - -impl RemoteLink { - pub async fn new( - config: Arc, - router_tx: Sender<(Id, Event)>, - mut network: Network, - ) -> Result<(String, Id, RemoteLink), Error> { - // Wait for MQTT connect packet and error out if it's not received in time to prevent - // DOS attacks by filling total connections that the server can handle with idle open - // connections which results in server rejecting new connections - let timeout = Duration::from_millis(config.connection_timeout_ms.into()); - let mut connect = time::timeout(timeout, async { - let connect = network.read_connect().await?; - - // Validate credentials if they exist - if let Some(credentials) = &config.login_credentials { - let validated = match &connect.login { - Some(l) => { - let mut validated = false; - - // Iterate through all the potential credentials - for entry in credentials { - if l.validate(&entry.username, &entry.password) { - validated = true; - break; - } - } - - validated - } - None => false, - }; - - // Return error if the username/password werenot found - if !validated { - return Err(Error::InvalidUsernameOrPassword); - } - } - - Ok::<_, Error>(connect) - }) - .await??; - - // Register this connection with the router. Router replys with ack which if ok will - // start the link. Router can sometimes reject the connection (ex max connection limit) - let client_id = connect.client_id.clone(); - let clean_session = connect.clean_session; - if !clean_session && client_id.is_empty() { - return Err(Error::InvalidClientId); - } - - let (mut connection, link_rx) = Connection::new_remote(&client_id, clean_session, 10); - - // Add last will to connection - if let Some(will) = connect.last_will.take() { - connection.set_will(will); - } - - let message = (0, Event::Connect(connection)); - router_tx.send(message).unwrap(); - - // TODO When a new connection request is sent to the router, router should ack with error - // TODO if it exceeds maximum allowed active connections - // Right now link identifies failure with dropped rx in router, which is probably ok for now - let (id, session, pending) = match link_rx.recv()? { - Notification::ConnectionAck(ack) => match ack { - ConnectionAck::Success((id, session, pending)) => (id, session, pending), - ConnectionAck::Failure(reason) => return Err(Error::ConnAck(reason)), - }, - message => return Err(Error::RouterMessage(message)), - }; - - // Send connection acknowledgement back to the client - let connack = ConnAck::new(ConnectReturnCode::Success, session); - network.connack(connack).await?; - - let max_inflight_count = config.max_inflight_count; - Ok(( - client_id, - id, - RemoteLink { - config, - connect, - id, - network, - router_tx, - link_rx, - total: 0, - pending, - state: State::new(max_inflight_count), - }, - )) - } - - pub async fn start(&mut self) -> Result<(), Error> { - self.network.set_keepalive(self.connect.keep_alive); - let pending = mem::take(&mut self.pending); - let mut pending = pending.into_iter(); - - loop { - select! { - o = self.network.readb(&mut self.state) => { - let disconnect = o?; - self.handle_network_data().await?; - - if disconnect { - return Err(Error::Disconnect) - } - } - // Receive from router when previous when state isn't in collision - // due to previously received data request - message = async { pending.next() } => { - match message { - Some(message) => self.handle_router_response(message).await?, - None => break - }; - } - } - } - - // Note: - // Shouldn't result in bounded queue deadlocks because of blocking n/w send - loop { - select! { - o = self.network.readb(&mut self.state) => { - let disconnect = o?; - self.handle_network_data().await?; - - if disconnect { - return Err(Error::Disconnect) - } - } - // Receive from router when previous when state isn't in collision - // due to previously received data request - message = self.link_rx.async_recv(), if !self.state.pause_outgoing() => { - let message = message?; - self.handle_router_response(message).await?; - } - } - } - } - - async fn handle_network_data(&mut self) -> Result<(), Error> { - let data = self.state.take_incoming(); - self.network.flush(self.state.write_mut()).await?; - - if !data.is_empty() { - debug!( - "{:11} {:14} Id = {}, Count = {}", - "data", - "remote", - self.id, - data.len() - ); - - let message = Event::Data(data); - self.router_tx.send((self.id, message))?; - } - - Ok(()) - } - - async fn handle_router_response(&mut self, message: Notification) -> Result<(), Error> { - match message { - Notification::ConnectionAck(_) => {} - Notification::Acks(reply) => { - trace!( - "{:11} {:14} Id = {}, Count = {}", - "acks", - "reply", - self.id, - reply.len() - ); - - for ack in reply.into_iter() { - self.state.outgoing_ack(ack)?; - } - } - Notification::Message(reply) => { - let topic = reply.topic; - let qos = qos(reply.qos).unwrap(); - let payload = reply.payload; - - trace!( - "{:11} {:14} Id = {}, Topic = {}, Count = {}", - "data", - "pending", - self.id, - topic, - payload.len() - ); - - self.state.add_pending(topic, qos, vec![payload]); - self.state.write_pending()?; - } - Notification::Data(reply) => { - let topic = reply.topic; - let qos = qos(reply.qos).unwrap(); - let payload = reply.payload; - - trace!( - "{:11} {:14} Id = {}, Topic = {}, Offsets = {:?}, Count = {}", - "data", - "reply", - self.id, - topic, - reply.cursor, - payload.len() - ); - - let payload_count = payload.len(); - if payload_count > self.config.max_inflight_count as usize { - return Err(Error::TooManyPayloads(payload_count)); - } - - self.total += payload_count; - self.state.add_pending(topic, qos, payload); - self.state.write_pending()?; - } - Notification::Pause => { - let message = (self.id, Event::Ready); - self.router_tx.send(message)?; - } - notification => { - warn!("{:?} not supported in remote link", notification); - } - } - - self.network.flush(self.state.write_mut()).await?; - Ok(()) - } -} diff --git a/rumqttd-old/src/rumqttlog/logs/acks.rs b/rumqttd-old/src/rumqttlog/logs/acks.rs deleted file mode 100644 index a41f13739..000000000 --- a/rumqttd-old/src/rumqttlog/logs/acks.rs +++ /dev/null @@ -1,67 +0,0 @@ -use crate::mqttbytes::v4::*; -use std::mem; - -/// Watermarks for a given topic -#[derive(Debug)] -pub struct Acks { - pending_acks_request: Option<()>, - /// Committed packet ids for acks - acks: Vec, -} - -impl Acks { - pub fn new() -> Acks { - Acks { - pending_acks_request: None, - acks: Vec::new(), - } - } - - pub fn handle_acks_request(&mut self) -> Option> { - let acks = self.acks(); - if acks.is_empty() { - return None; - } - - Some(acks) - } - - pub fn register_pending_acks_request(&mut self) { - self.pending_acks_request = Some(()); - } - - pub fn take_pending_acks_request(&mut self) -> Option<()> { - self.pending_acks_request.take() - } - - pub fn push_publish_ack(&mut self, pkid: u16, qos: u8) { - match qos { - 1 => self.acks.push(Packet::PubAck(PubAck::new(pkid))), - 2 => self.acks.push(Packet::PubRec(PubRec::new(pkid))), - _ => {} - } - } - - pub fn push_subscribe_ack(&mut self, pkid: u16, return_codes: Vec) { - let suback = SubAck::new(pkid, return_codes); - let suback = Packet::SubAck(suback); - self.acks.push(suback); - } - - pub fn push_unsubscribe_ack(&mut self, pkid: u16) { - let unsuback = UnsubAck::new(pkid); - let unsuback = Packet::UnsubAck(unsuback); - self.acks.push(unsuback); - } - - /// Returns committed acks by take - pub fn acks(&mut self) -> Vec { - mem::take(&mut self.acks) - } -} - -impl Default for Acks { - fn default() -> Self { - Self::new() - } -} diff --git a/rumqttd-old/src/rumqttlog/logs/connections.rs b/rumqttd-old/src/rumqttlog/logs/connections.rs deleted file mode 100644 index 58b583f4e..000000000 --- a/rumqttd-old/src/rumqttlog/logs/connections.rs +++ /dev/null @@ -1,68 +0,0 @@ -use std::collections::HashMap; - -use crate::rumqttlog::{router::Tracker, ConnectionId, Notification}; - -struct SavedState { - id: ConnectionId, - tracker: Option, - pending: Option>, -} - -pub struct ConnectionsLog { - connections: HashMap, -} - -impl ConnectionsLog { - pub fn new() -> ConnectionsLog { - ConnectionsLog { - connections: HashMap::new(), - } - } - - pub fn id(&self, id: &str) -> Option { - self.connections.get(id).map(|v| v.id) - } - - pub fn add( - &mut self, - id: &str, - connection_id: ConnectionId, - ) -> (Option, Option>) { - match self.connections.get_mut(id) { - // Return tracker of previous connection for persistent connection - Some(savedstate) => { - savedstate.id = connection_id; - (savedstate.tracker.take(), savedstate.pending.take()) - } - // Add new connection if this is the first connection with this id - None => { - self.connections.insert( - id.to_owned(), - SavedState { - id: connection_id, - tracker: None, - pending: None, - }, - ); - - (None, None) - } - } - } - - pub fn save(&mut self, id: &str, mut tracker: Tracker, pending: Vec) { - tracker.set_busy_unschedule(false); - tracker.set_empty_unschedule(false); - - if let Some(graveyard) = self.connections.get_mut(id) { - graveyard.tracker = Some(tracker); - graveyard.pending = Some(pending); - } - } -} - -impl Default for ConnectionsLog { - fn default() -> Self { - Self::new() - } -} diff --git a/rumqttd-old/src/rumqttlog/logs/data.rs b/rumqttd-old/src/rumqttlog/logs/data.rs deleted file mode 100644 index ba2fae1f3..000000000 --- a/rumqttd-old/src/rumqttlog/logs/data.rs +++ /dev/null @@ -1,133 +0,0 @@ -use std::collections::HashMap; -use std::io; - -use crate::rumqttlog::Config; -use bytes::Bytes; -use segments::MemoryLog; -use std::sync::Arc; - -pub(crate) struct DataLog { - config: Arc, - logs: HashMap, -} - -struct Data { - retained: Option<(u64, Bytes)>, - log: MemoryLog, -} - -impl DataLog { - pub fn new(config: Arc) -> DataLog { - DataLog { - config, - logs: HashMap::new(), - } - } - - /// Appends the record to correct commitlog and returns a boolean to indicate - /// if this topic is new along with the offset of append - pub fn append(&mut self, topic: &str, record: Bytes) -> io::Result<(bool, (u64, u64))> { - // Entry instead of if/else? - if let Some(data) = self.logs.get_mut(topic) { - let offsets = data.log.append(record.len(), record); - Ok((false, offsets)) - } else { - let max_segment_size = self.config.max_segment_size; - let max_segment_count = self.config.max_segment_count; - let mut data = Data { - retained: None, - log: MemoryLog::new(max_segment_size, max_segment_count), - }; - let offsets = data.log.append(record.len(), record); - self.logs.insert(topic.to_owned(), data); - Ok((true, offsets)) - } - } - - /// Updates retain record to and returns a boolean to indicate - /// if this topic is new along with the offset of append - pub fn retain(&mut self, topic: &str, record: Bytes) -> io::Result { - if let Some(log) = self.logs.get_mut(topic) { - if record.is_empty() { - log.retained = None; - } else { - let retained = log.retained.get_or_insert((0, record.clone())); - retained.0 += 1; - retained.1 = record; - } - - Ok(false) - } else { - let max_segment_size = self.config.max_segment_size; - let max_segment_count = self.config.max_segment_count; - let mut data = Data { - retained: None, - log: MemoryLog::new(max_segment_size, max_segment_count), - }; - - if record.is_empty() { - data.retained = None; - } else { - let retained = data.retained.get_or_insert((0, record.clone())); - retained.0 += 1; - retained.1 = record; - } - - self.logs.insert(topic.to_owned(), data); - Ok(true) - } - } - - fn next_offset(&self, topic: &str) -> Option<(u64, u64)> { - let data = match self.logs.get(topic) { - Some(log) => log, - None => return None, - }; - - Some(data.log.next_offset()) - } - - pub fn seek_offsets_to_end(&self, topic: &mut (String, u8, (u64, u64))) { - if let Some(last_offset) = self.next_offset(&topic.0) { - topic.2 = last_offset; - } - } - - // TODO: remove once type complexity is dealt with, added to silence clippy - #[allow(clippy::type_complexity)] - pub fn readv( - &mut self, - topic: &str, - in_segment: u64, - in_offset: u64, - last_retain: u64, - ) -> io::Result, u64, u64, u64, Vec)>> { - // Router during data request and notifications will check both - // native and replica commitlog where this topic doesn't exist - let data = match self.logs.get_mut(topic) { - Some(log) => log, - None => return Ok(None), - }; - - let (jump, segment, offset, mut out) = data.log.readv(in_segment, in_offset); - - let mut last_retain = last_retain; - if let Some((id, publish)) = &mut data.retained { - if *id != last_retain { - out.push(publish.clone()); - last_retain = *id; - } - } - - // For debugging. Will be removed later - // println!( - // "In: segment {} offset {}, Out: segment {} offset {}, Count {}", - // in_segment, - // in_offset, - // segment, - // offset, - // data.len() - // ); - Ok(Some((jump, segment, offset, last_retain, out))) - } -} diff --git a/rumqttd-old/src/rumqttlog/logs/mod.rs b/rumqttd-old/src/rumqttlog/logs/mod.rs deleted file mode 100644 index 1bfc268a4..000000000 --- a/rumqttd-old/src/rumqttlog/logs/mod.rs +++ /dev/null @@ -1,91 +0,0 @@ -pub mod acks; -mod connections; -mod data; -mod topics; - -use crate::rumqttlog::{Config, Data, DataRequest}; -use bytes::Bytes; -use log::error; -use std::sync::Arc; - -pub use connections::ConnectionsLog; -pub use topics::TopicsLog; - -pub struct DataLog { - commitlog: data::DataLog, -} - -impl DataLog { - pub fn new(config: Arc) -> DataLog { - let commitlog = data::DataLog::new(config); - DataLog { commitlog } - } - - /// Update matched topic offsets to current offset of this topic's commitlog - pub fn seek_offsets_to_end(&self, topic: &mut (String, u8, (u64, u64))) { - self.commitlog.seek_offsets_to_end(topic); - } - - /// Connections pull logs from both replication and connections where as replicator - /// only pull logs from connections. - /// Data from replicator and data from connection are separated for this reason - pub fn append(&mut self, topic: &str, bytes: Bytes) -> Option<(bool, (u64, u64))> { - match self.commitlog.append(topic, bytes) { - Ok(v) => Some(v), - Err(e) => { - error!("Commitlog append failed. Error = {:?}", e); - None - } - } - } - - pub fn retain(&mut self, topic: &str, bytes: Bytes) -> Option { - // id 0-10 are reserved for replications which are linked to other routers in the mesh - match self.commitlog.retain(topic, bytes) { - Ok(v) => Some(v), - Err(e) => { - error!("Commitlog append failed. Error = {:?}", e); - None - } - } - } - - /// Extracts data from native and replicated logs. Returns None in case the - /// log is caught up or encountered an error while reading data - pub(crate) fn extract_data(&mut self, request: &DataRequest) -> Option { - let topic = &request.topic; - let mut last_retain = request.last_retain; - - // Iterate through native and replica commitlogs to collect data (of a topic) - let (segment, offset) = request.cursor; - match self.commitlog.readv(topic, segment, offset, last_retain) { - Ok(Some(v)) => { - let (jump, base_offset, record_offset, retain, data) = v; - let cursor = match jump { - Some(next) => (next, next), - None => (base_offset, record_offset), - }; - - // Update retain id (incase readv has retained publish to consider) - last_retain = retain; - if data.is_empty() { - return None; - } - - Some(Data::new( - request.topic.clone(), - request.qos, - cursor, - last_retain, - 0, - data, - )) - } - Ok(None) => None, - Err(e) => { - error!("Failed to extract data from commitlog. Error = {:?}", e); - None - } - } - } -} diff --git a/rumqttd-old/src/rumqttlog/logs/topics.rs b/rumqttd-old/src/rumqttlog/logs/topics.rs deleted file mode 100644 index 934035018..000000000 --- a/rumqttd-old/src/rumqttlog/logs/topics.rs +++ /dev/null @@ -1,46 +0,0 @@ -/// A temporal list of unique new topics -#[derive(Debug)] -pub struct TopicsLog { - /// List of new topics - topics: Vec, -} - -impl TopicsLog { - /// Create a new topic log - pub fn new() -> TopicsLog { - TopicsLog { topics: Vec::new() } - } - - /// read n topics from a give offset along with offset of the last read topic - pub fn readv(&self, offset: usize, count: usize) -> Option<(usize, &[String])> { - let len = self.topics.len(); - if offset >= len { - return None; - } - - // read till the end if the count is 0 - let mut next_offset = if count == 0 { len } else { offset + count }; - - if next_offset >= len { - next_offset = len; - } - - let out = self.topics[offset..next_offset].as_ref(); - if out.is_empty() { - return None; - } - - Some((next_offset, out)) - } - - /// Appends the topic if the topic isn't already seen - pub fn append(&mut self, topic: &str) { - self.topics.push(topic.to_owned()); - } -} - -impl Default for TopicsLog { - fn default() -> Self { - Self::new() - } -} diff --git a/rumqttd-old/src/rumqttlog/mod.rs b/rumqttd-old/src/rumqttlog/mod.rs deleted file mode 100644 index afc49c12c..000000000 --- a/rumqttd-old/src/rumqttlog/mod.rs +++ /dev/null @@ -1,40 +0,0 @@ -pub mod logs; -pub mod router; -pub mod waiters; - -use std::path::PathBuf; - -pub use router::connection::Connection; -pub use router::{ - ConnectionAck, Data, DataRequest, Disconnection, Event, Message, MetricsReply, MetricsRequest, - Notification, Router, -}; - -pub use jackiechan::{bounded, Receiver, RecvError, RecvTimeoutError, SendError, Sender}; -use serde::{Deserialize, Serialize}; - -pub type ConnectionId = usize; -pub type RouterId = usize; -pub type Topic = String; -pub type TopicId = usize; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Config { - pub id: usize, - pub dir: PathBuf, - pub max_segment_size: usize, - pub max_segment_count: usize, - pub max_connections: usize, -} - -impl Default for Config { - fn default() -> Self { - Config { - id: 255, - dir: PathBuf::from("/tmp/timestone"), - max_segment_size: 5 * 1024 * 1024, - max_segment_count: 1024, - max_connections: 1010, - } - } -} diff --git a/rumqttd-old/src/rumqttlog/router/connection.rs b/rumqttd-old/src/rumqttlog/router/connection.rs deleted file mode 100644 index dfc0ce282..000000000 --- a/rumqttd-old/src/rumqttlog/router/connection.rs +++ /dev/null @@ -1,130 +0,0 @@ -use crate::mqttbytes::v4::LastWill; -use jackiechan::{bounded, Receiver, Sender, TrySendError}; -use std::fmt; - -use super::Notification; - -#[derive(Debug, Clone)] -pub enum ConnectionType { - Device(String), - Replicator(usize), -} - -/// Used to register a new connection with the router -/// Connection messages encompasses a handle for router to -/// communicate with this connection -pub struct Connection { - /// Kind of connection. A replicator connection or a device connection - /// Replicator connection are only created from inside this library. - /// All the external connections are of 'device' type - pub conn: ConnectionType, - /// Clean session - clean: bool, - /// Connection will - will: Option, - /// Last failed message to connection - last_failed: Option, - /// Handle which is given to router to allow router to comminicate with - /// this connection - handle: Sender, - /// Capacity of the channel - capacity: usize, - /// Last seen remaining space in the channel - remaining_space: usize, -} - -impl Connection { - pub fn new_remote( - id: &str, - clean: bool, - capacity: usize, - ) -> (Box, Receiver) { - let (this_tx, this_rx) = bounded(capacity); - - let connection = Connection { - conn: ConnectionType::Device(id.to_owned()), - clean, - will: None, - last_failed: None, - handle: this_tx, - capacity, - remaining_space: capacity, - }; - - (Box::new(connection), this_rx) - } - - pub fn new_replica( - id: usize, - clean: bool, - capacity: usize, - ) -> (Connection, Receiver) { - let (this_tx, this_rx) = bounded(capacity); - - let connection = Connection { - conn: ConnectionType::Replicator(id), - clean, - will: None, - last_failed: None, - handle: this_tx, - capacity, - remaining_space: capacity, - }; - - (connection, this_rx) - } - - pub fn clean(&self) -> bool { - self.clean - } - - pub fn will(&mut self) -> Option { - self.will.take() - } - - pub fn set_will(&mut self, will: LastWill) { - self.will = Some(will); - } - - /// Sends notification and returns status to unschedule this connection - pub fn notify(&mut self, notification: Notification) -> bool { - if let Err(e) = self.handle.try_send(notification) { - match e { - TrySendError::Full(_e) => unreachable!(), - TrySendError::Closed(e) => { - self.last_failed = Some(e); - return true; - } - } - } - - self.remaining_space -= 1; - - // Update remaining space if there is room for only one notification. - if self.remaining_space <= 1 { - self.remaining_space = self.capacity - self.handle.len(); - - // If remaining space is still 1 after refresh, send pause notification - // to the connection and return unschedule true - if self.remaining_space <= 1 { - let notification = Notification::Pause; - if let Err(e) = self.handle.try_send(notification) { - match e { - TrySendError::Full(_) => unreachable!(), - TrySendError::Closed(e) => self.last_failed = Some(e), - } - } - - return true; - } - } - - false - } -} - -impl fmt::Debug for Connection { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self.conn) - } -} diff --git a/rumqttd-old/src/rumqttlog/router/metrics.rs b/rumqttd-old/src/rumqttlog/router/metrics.rs deleted file mode 100644 index 49188b20d..000000000 --- a/rumqttd-old/src/rumqttlog/router/metrics.rs +++ /dev/null @@ -1,51 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::sync::Arc; - -use crate::rumqttlog::{Config, RouterId}; - -use super::Tracker; - -#[derive(Debug, Clone)] -pub enum MetricsRequest { - Config, - Router, - Connection(String), -} - -#[derive(Debug, Clone)] -pub enum MetricsReply { - Config(Arc), - Router(RouterMetrics), - Connection(ConnectionMetrics), -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct RouterMetrics { - pub router_id: RouterId, - pub total_connections: usize, - pub total_topics: usize, - pub total_subscriptions: usize, -} - -impl RouterMetrics { - pub fn new(router_id: RouterId) -> RouterMetrics { - RouterMetrics { - router_id, - total_connections: 0, - total_topics: 0, - total_subscriptions: 0, - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ConnectionMetrics { - id: String, - tracker: Option, -} - -impl ConnectionMetrics { - pub fn new(id: String, tracker: Option) -> ConnectionMetrics { - ConnectionMetrics { id, tracker } - } -} diff --git a/rumqttd-old/src/rumqttlog/router/mod.rs b/rumqttd-old/src/rumqttlog/router/mod.rs deleted file mode 100644 index d7b339392..000000000 --- a/rumqttd-old/src/rumqttlog/router/mod.rs +++ /dev/null @@ -1,276 +0,0 @@ -extern crate bytes; - -pub(crate) mod connection; -mod metrics; -mod readyqueue; -// TODO: remove clippy once renamed, added to silence clippy -#[allow(clippy::module_inception)] -mod router; -mod slab; -mod tracker; - -use connection::Connection; -pub use router::Router; -pub use tracker::Tracker; - -use self::bytes::Bytes; -use crate::mqttbytes::v4::Packet; -pub use metrics::{ConnectionMetrics, MetricsReply, MetricsRequest}; -use serde::{Deserialize, Serialize}; -use std::fmt; - -/// Messages from connection to router -#[derive(Debug)] -pub enum Event { - /// Client id and connection handle - Connect(Box), - /// Connection ready to receive more data - Ready, - /// Data for native commitlog - Data(Vec), - /// Disconnection request - Disconnect(Disconnection), - /// Get metrics of a connection or all connections - Metrics(MetricsRequest), -} - -/// Requests for pull operations -#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] -pub enum Request { - /// Data request - Data(DataRequest), - /// Topics request - Topics(TopicsRequest), - /// Acks request - Acks(AcksRequest), -} - -/// Notification from router to connection -#[derive(Debug)] -pub enum Notification { - /// Connection reply - ConnectionAck(ConnectionAck), - /// Individual publish - Message(Message), - /// Data reply - Data(Data), - /// Watermarks reply - Acks(Vec), - /// Connection paused by router - Pause, - /// All metrics - Metrics(MetricsReply), -} - -/// Request that connection/linker makes to extract data from commitlog -/// NOTE Connection can make one sweep request to get data from multiple topics -/// but we'll keep it simple for now as multiple requests in one message can -/// makes constant extraction size harder -#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct DataRequest { - /// Log to sweep - pub(crate) topic: String, - /// QoS of the request - pub(crate) qos: u8, - /// (segment, offset) tuples per replica (1 native and 2 replicas) - pub(crate) cursor: (u64, u64), - /// Last retain id - pub(crate) last_retain: u64, - /// Maximum count of payload buffer per replica - max_count: usize, -} - -impl DataRequest { - /// New data request with offsets starting from 0 - pub fn new(topic: String, qos: u8) -> DataRequest { - DataRequest { - topic, - qos, - cursor: (0, 0), - last_retain: 0, - max_count: 100, - } - } - - /// New data request with provided offsets - pub fn offsets(topic: String, qos: u8, cursor: (u64, u64), last_retain: u64) -> DataRequest { - DataRequest { - topic, - qos, - cursor, - last_retain, - max_count: 100, - } - } -} - -impl fmt::Debug for DataRequest { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "Topic = {}, cursors = {:?}, max_count = {}", - self.topic, self.cursor, self.max_count - ) - } -} - -pub struct Message { - /// Log to sweep - pub topic: String, - /// Qos of the topic - pub qos: u8, - /// Reply data chain - pub payload: Bytes, -} - -impl Message { - pub fn new(topic: String, qos: u8, payload: Bytes) -> Message { - Message { - topic, - qos, - payload, - } - } -} - -impl fmt::Debug for Message { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "Topic = {:?}, Payload size = {}", - self.topic, - self.payload.len() - ) - } -} - -pub struct Data { - /// Log to sweep - pub topic: String, - /// Qos of the topic - pub qos: u8, - /// (segment, offset) tuples per replica (1 native and 2 replicas) - pub cursor: (u64, u64), - /// Next retain publish id - pub last_retain: u64, - /// Payload size - pub size: usize, - /// Reply data chain - pub payload: Vec, -} - -impl Data { - pub fn new( - topic: String, - qos: u8, - cursor: (u64, u64), - last_retain: u64, - size: usize, - payload: Vec, - ) -> Data { - Data { - topic, - qos, - cursor, - last_retain, - size, - payload, - } - } -} - -impl fmt::Debug for Data { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "Topic = {:?}, Cursors = {:?}, Payload size = {}, Payload count = {}", - self.topic, - self.cursor, - self.size, - self.payload.len() - ) - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct TopicsRequest { - /// Start from this offset - offset: usize, - /// Maximum number of topics to read - count: usize, -} - -impl TopicsRequest { - pub fn new() -> TopicsRequest { - TopicsRequest { - count: 10, - offset: 0, - } - } - - pub fn set_count(&mut self, count: usize) { - self.count = count; - } - - pub fn offset(offset: usize) -> TopicsRequest { - TopicsRequest { offset, count: 10 } - } -} - -impl Default for TopicsRequest { - fn default() -> Self { - Self::new() - } -} - -pub struct Topics<'a> { - offset: usize, - topics: &'a [String], -} - -impl<'a> Topics<'a> { - pub fn new(offset: usize, topics: &'a [String]) -> Topics { - Topics { offset, topics } - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AcksRequest; - -impl AcksRequest { - pub fn new() -> AcksRequest { - AcksRequest - } -} - -impl Default for AcksRequest { - fn default() -> Self { - Self::new() - } -} - -#[derive(Debug)] -pub enum ConnectionAck { - /// Id assigned by the router for this connection and - /// previous session status - Success((usize, bool, Vec)), - /// Failure and reason for failure string - Failure(String), -} - -#[derive(Debug)] -pub struct Disconnection { - id: String, - execute_will: bool, - pending: Vec, -} - -impl Disconnection { - pub fn new(id: String, execute_will: bool, pending: Vec) -> Disconnection { - Disconnection { - id, - execute_will, - pending, - } - } -} diff --git a/rumqttd-old/src/rumqttlog/router/readyqueue.rs b/rumqttd-old/src/rumqttlog/router/readyqueue.rs deleted file mode 100644 index 8e75a9e75..000000000 --- a/rumqttd-old/src/rumqttlog/router/readyqueue.rs +++ /dev/null @@ -1,35 +0,0 @@ -use std::collections::VecDeque; - -use crate::rumqttlog::ConnectionId; - -#[derive(Debug)] -pub struct ReadyQueue { - queue: VecDeque, -} - -impl ReadyQueue { - pub fn new() -> ReadyQueue { - ReadyQueue { - queue: VecDeque::with_capacity(100), - } - } - - pub fn is_empty(&self) -> bool { - self.queue.is_empty() - } - - pub fn pop_front(&mut self) -> Option { - self.queue.pop_front() - } - - pub fn push_back(&mut self, id: ConnectionId) { - self.queue.push_back(id); - } - - /// Remove a connection from waiters - pub fn remove(&mut self, id: ConnectionId) { - if let Some(index) = self.queue.iter().position(|x| *x == id) { - self.queue.swap_remove_back(index); - } - } -} diff --git a/rumqttd-old/src/rumqttlog/router/router.rs b/rumqttd-old/src/rumqttlog/router/router.rs deleted file mode 100644 index a6454fdeb..000000000 --- a/rumqttd-old/src/rumqttlog/router/router.rs +++ /dev/null @@ -1,861 +0,0 @@ -use std::sync::Arc; - -use crate::mqttbytes::v4::{Packet, Publish, Subscribe, SubscribeReasonCode, Unsubscribe}; -use crate::rumqttlog::logs::acks::Acks; -use crate::rumqttlog::logs::{ConnectionsLog, DataLog, TopicsLog}; -use crate::rumqttlog::waiters::{DataWaiters, TopicsWaiters}; -use crate::rumqttlog::{Config, ConnectionId, RouterId}; -use jackiechan::{bounded, Receiver, RecvError, Sender, TryRecvError}; -use log::{error, info, trace, warn}; -use thiserror::Error; - -use super::connection::ConnectionType; -use super::metrics::RouterMetrics; -use super::readyqueue::ReadyQueue; -use super::slab::Slab; -use super::*; - -#[derive(Error, Debug)] -#[error("...")] -pub enum RouterError { - Recv(#[from] RecvError), - Disconnected, -} - -pub struct Router { - /// Router configuration - config: Arc, - /// Id of this router. Used to index native commitlog to store data from - /// local connections - _id: RouterId, - /// Connections log to handle persitent session and synchronize connection - /// information between nodes - connectionslog: ConnectionsLog, - /// Data logs grouped by replica - datalog: DataLog, - /// Topic log - topicslog: TopicsLog, - /// Pre-allocated list of connections - connections: Slab>, - /// Subscriptions and matching topics maintained per connection - trackers: Slab, - /// Watermarks of a connection - watermarks: Slab, - /// Connections with more pending requests and ready to make progress - readyqueue: ReadyQueue, - /// Waiter on a topic. These are used to wake connections/replicators - /// which are caught up all the data on a topic. Map: topic -> List[Connection Ids] - data_waiters: DataWaiters, - /// Waiters on new topics - topics_waiters: TopicsWaiters, - /// Channel receiver to receive data from all the active connections and - /// replicators. Each connection will have a tx handle which they use - /// to send data and requests to router - router_rx: Receiver<(ConnectionId, Event)>, - /// Aggregates for all connections - metrics: RouterMetrics, -} - -impl Router { - pub fn new(config: Arc) -> (Self, Sender<(ConnectionId, Event)>) { - let (router_tx, router_rx) = bounded(1000); - let id = config.id; - let max_connections = config.max_connections; - - // Connection level information - let connections = Slab::with_capacity(max_connections); - let trackers = Slab::with_capacity(max_connections); - let watermarks = Slab::with_capacity(max_connections); - - // Global data - let connectionslog = ConnectionsLog::new(); - let datalog: DataLog = DataLog::new(config.clone()); - let topicslog = TopicsLog::new(); - - // Waiters to notify new data or topics - let data_waiters = DataWaiters::new(); - let topics_waiters = TopicsWaiters::new(); - let readyqueue = ReadyQueue::new(); - let metrics = RouterMetrics::new(id); - - let router = Router { - config, - _id: id, - connectionslog, - datalog, - topicslog, - connections, - trackers, - watermarks, - readyqueue, - data_waiters, - topics_waiters, - router_rx, - metrics, - }; - - (router, router_tx) - } - - /// Waits on incoming events when ready queue is empty. - /// After pulling 1 event, tries to pull 500 more events - /// before polling ready queue 100 times (connections) - pub fn start(&mut self) -> Result<(), RouterError> { - loop { - // Block on incoming events if there are no connections - // in ready queue. - if self.readyqueue.is_empty() { - let (id, data) = self.router_rx.recv()?; - self.route(id, data); - } - - // Try reading more from connections in a non-blocking - // fashion to accumulate data and handle subscriptions. - // Accumulating more data lets requests retrieve bigger - // bulks which in turn increases efficiency - for _ in 0..500 { - // All these methods will handle state and errors - match self.router_rx.try_recv() { - Ok((id, data)) => self.route(id, data), - Err(TryRecvError::Closed) => return Err(RouterError::Disconnected), - Err(TryRecvError::Empty) => break, - } - } - - // Poll 100 connections which are ready in ready queue - for _ in 0..100 { - match self.readyqueue.pop_front() { - Some(id) => self.connection_ready(id, 100), - None => break, - } - } - } - } - - fn route(&mut self, id: usize, data: Event) { - match data { - Event::Connect(connection) => self.handle_new_connection(connection), - Event::Data(data) => self.handle_connection_data(id, data), - Event::Disconnect(request) => self.handle_disconnection(id, request), - Event::Ready => self.connection_ready(id, 100), - Event::Metrics(metrics) => self.retrieve_metrics(id, metrics), - } - } - - fn retrieve_metrics(&mut self, id: ConnectionId, metrics: MetricsRequest) { - info!("{:11} {:14} Id = {}", "console", "metrics", id); - let message = match metrics { - MetricsRequest::Config => { - Notification::Metrics(MetricsReply::Config(self.config.clone())) - } - MetricsRequest::Router => { - Notification::Metrics(MetricsReply::Router(self.metrics.clone())) - } - MetricsRequest::Connection(device_id) => { - let tracker = match self.connectionslog.id(&device_id) { - Some(id) => self.trackers.get_mut(id).cloned(), - None => None, - }; - - Notification::Metrics(MetricsReply::Connection(ConnectionMetrics::new( - device_id, tracker, - ))) - } - }; - - notify(&mut self.connections, id, message); - } - - fn handle_new_connection(&mut self, connection: Box) { - let clean = connection.clean(); - - let (id, mut tracker, mut pending) = match connection.conn.clone() { - ConnectionType::Replicator(id) => { - info!("{:11} {:14} Id = {}", "connection", "replicator", id,); - self.connections.insert_at(connection, id); - (id, None, None) - } - ConnectionType::Device(did) => match self.connections.insert(connection) { - Some(id) => { - info!("{:11} {:14} Id = {}:{}", "connection", "remote", did, id); - let (tracker, pending) = self.connectionslog.add(&did, id); - (id, tracker, pending) - } - None => { - error!("No space for new connection!!"); - return; - } - }, - }; - - let previous_session = tracker.is_some(); - - // Add a new tracker or resume from previous topics and offsets - match tracker.take() { - Some(tracker) if !clean => self.trackers.insert_at(tracker, id), - _ => self.trackers.insert_at(Tracker::new(), id), - } - - let ack = match pending.take() { - Some(pending) => ConnectionAck::Success((id, previous_session, pending)), - None => ConnectionAck::Success((id, previous_session, Vec::new())), - }; - - self.watermarks.insert_at(Acks::new(), id); - self.readyqueue.push_back(id); - - let message = Notification::ConnectionAck(ack); - notify(&mut self.connections, id, message); - } - - fn handle_disconnection(&mut self, id: ConnectionId, disconnect: Disconnection) { - let did = disconnect.id; - let execute_will = disconnect.execute_will; - let pending = disconnect.pending; - - info!("{:11} {:14} Id = {}:{}", "disconnect", "", did, id); - - // Forward connection will - let mut connection = self.connections.remove(id).unwrap(); - let clean = connection.clean(); - - if execute_will { - if let Some(will) = connection.will() { - let publish = Publish::from_bytes(will.topic, will.qos, will.message); - self.handle_connection_publish(id, publish); - } - } - - let mut tracker = self.trackers.remove(id); - let inflight_data_requests = self.data_waiters.remove(id); - let mut inflight_topics_request = self.topics_waiters.remove(id); - self.watermarks.remove(id); - self.readyqueue.remove(id); - - if !clean { - if let Some(mut tracker) = tracker.take() { - // Add inflight data requests back to tracker - for request in inflight_data_requests { - tracker.register_data_request(request); - } - - // Add inflight topics request back to tracker - if let Some(request) = inflight_topics_request.take() { - tracker.register_topics_request(request); - } - - // Add acks request. This might be a duplicate - // TODO Get this from 'watermarks.remove' - tracker.register_acks_request(); - - // Save tracker - self.connectionslog.save(&did, tracker, pending); - } - } - } - - fn connection_ready(&mut self, id: ConnectionId, max_iterations: usize) { - trace!("{:11} {:14} Id = {}", "requests", "start", id,); - let tracker = self.trackers.get_mut(id).unwrap(); - if tracker.busy_unschedule() { - tracker.set_busy_unschedule(false); - } - - // Iterate through a max of 'max_iterations' requests everytime a connection. - // if polled. This prevents a connection from unfairly taking up router's time - // preventing other connections from making progress. - for _ in 0..max_iterations { - match tracker.pop_request() { - Some(request) => match request { - Request::Data(request) => { - let datalog = &mut self.datalog; - let waiters = &mut self.data_waiters; - - // Get data from commitlog and register for notification if - // all the data is caught up. - if let Some(data) = handle_data_request(id, request, datalog, waiters) { - // If data is yielded by commitlog, register a new data request - // in the tracker with next offset and send data notification to - // the connection - let topic = data.topic.clone(); - let qos = data.qos; - let cursors = data.cursor; - let last_retain = data.last_retain; - - let request = DataRequest::offsets(topic, qos, cursors, last_retain); - tracker.register_data_request(request); - let notification = Notification::Data(data); - let pause = notify(&mut self.connections, id, notification); - - // This connection might not be able to process next request. Don't schedule - if pause { - info!("Connection busy. Unschedule. Id = {}", id); - tracker.set_busy_unschedule(true); - return; - } - } - } - Request::Topics(request) => { - let topicslog = &mut self.topicslog; - let waiters = &mut self.topics_waiters; - - // Get topics from topics log and register for notification if - // all the topics are caught up - if let Some(data) = handle_topics_request(id, request, topicslog, waiters) { - // Register for new topics request if previous request is handled - tracker.track_matched_topics(data.topics); - tracker.register_topics_request(TopicsRequest::offset(data.offset)); - } - } - Request::Acks(_) => { - // Get acks from commitlog and register for notification if all - // the data is caught up. - let acks = self.watermarks.get_mut(id).unwrap(); - - // If acks are yielded, register a new acks request - // and send acks notification to the connection - if let Some(acks) = handle_acks_request(id, acks) { - tracker.register_acks_request(); - let notification = Notification::Acks(acks); - let pause = notify(&mut self.connections, id, notification); - - // This connection might not be able to process next request. Don't schedule - if pause { - info!("Connection busy/closed. Unschedule. Id = {}", id); - tracker.set_busy_unschedule(true); - return; - } - } - } - }, - None => { - // At this point, pending requests in tracker is 0. Early return - // here prevents connection from being added to ready queue again. - // New requests are added to tracker through notifications of - // previous requests. When first request is added back to tracker - // again, it's scheduled back on ready queue again. - trace!("{:11} {:14} Id = {}", "requests", "done", id); - tracker.set_empty_unschedule(true); - return; - } - } - } - - // If there are more requests in the tracker, add the connection back - // to ready queue. - trace!("{:11} {:14} Id = {}", "requests", "pause", id,); - self.readyqueue.push_back(id); - } - - /// Handles new incoming data on a topic - fn handle_connection_data(&mut self, id: ConnectionId, data: Vec) { - trace!( - "{:11} {:14} Id = {} Count = {}", - "data", - "incoming", - id, - data.len() - ); - - for publish in data { - match publish { - Packet::Publish(publish) => self.handle_connection_publish(id, publish), - Packet::Subscribe(subscribe) => self.handle_connection_subscribe(id, subscribe), - Packet::Unsubscribe(unsubscribe) => { - self.handle_connection_unsubscribe(id, unsubscribe) - } - incoming => { - warn!("Packet = {:?} not supported by router yet", incoming); - } - } - } - - trace!("{:11} {:14} Id = {}", "data", "committed", id,); - } - - fn handle_connection_subscribe(&mut self, id: ConnectionId, subscribe: Subscribe) { - trace!( - "{:11} {:14} Id = {} Filters = {:?}", - "data", - "subscribe", - id, - subscribe.filters - ); - - let topics = self.topicslog.readv(0, 0); - let tracker = self.trackers.get_mut(id).unwrap(); - - let mut return_codes = Vec::new(); - for filter in subscribe.filters.iter() { - if filter.path.starts_with("test") || filter.path.starts_with('$') { - return_codes.push(SubscribeReasonCode::Failure); - } else { - return_codes.push(SubscribeReasonCode::Success(filter.qos)); - } - } - - // A new subscription should match with all the existing topics and take a snapshot of current - // offset of all the matched topics. Subscribers will receive data from the next offset - match topics { - Some((_, topics)) => { - // Add subscription and get topics matching all the existing topics - // in the (topics) commitlog ant seek them to next offset. Add subscriptions - // and store matched topics interna. If this is the first subscription, - // register topics request - if tracker.add_subscription_and_match(subscribe.filters, topics) { - tracker.register_topics_request(TopicsRequest::offset(topics.len())); - - // If connection is removed from ready queue because of 0 requests, - // but connection itself is ready for more notifications, add - // connection back to ready queue - if tracker.empty_unschedule() { - self.readyqueue.push_back(id); - tracker.set_empty_unschedule(false); - } - } - - // Take matched topics above and seek their offsets. Seeking is - // necessary because new subscription should yield only subsequent data - // FIXME: Verify logic of self.id. Should this be seeked for replicators as well? - while let Some(mut topic) = tracker.next_matched() { - self.datalog.seek_offsets_to_end(&mut topic); - let (topic, qos, cursors) = (topic.0, topic.1, topic.2); - let request = DataRequest::offsets(topic, qos, cursors, 0); - tracker.register_data_request(request); - - // If connection is removed from ready queue because of 0 requests, - // but connection itself is ready for more notifications, add - // connection back to ready queue - if tracker.empty_unschedule() { - self.readyqueue.push_back(id); - tracker.set_empty_unschedule(false); - } - } - } - None => { - // Router did not receive data from any topics yet. Add subscription and - // register topics request from offset 0 - if tracker.add_subscription_and_match(subscribe.filters, &[]) { - tracker.register_topics_request(TopicsRequest::offset(0)); - - // If connection is removed from ready queue because of 0 requests, - // but connection itself is ready for more notifications, add - // connection back to ready queue - if tracker.empty_unschedule() { - self.readyqueue.push_back(id); - tracker.set_empty_unschedule(false); - } - } - } - }; - - // Update acks and triggers acks notification for suback - let watermarks = self.watermarks.get_mut(id).unwrap(); - watermarks.push_subscribe_ack(subscribe.pkid, return_codes); - self.fresh_acks_notification(id); - } - - fn handle_connection_unsubscribe(&mut self, id: ConnectionId, unsubscribe: Unsubscribe) { - trace!( - "{:11} {:14} Id = {} Filters = {:?}", - "data", - "unsubscribe", - id, - unsubscribe.topics - ); - - let tracker = self.trackers.get_mut(id).unwrap(); - let inflight = tracker.remove_subscription_and_unmatch(unsubscribe.topics); - - for topic in inflight.into_iter() { - if let Some(waiters) = self.data_waiters.get_mut(&topic) { - waiters.remove(id); - } - } - - // Update acks and triggers acks notification for suback - let watermarks = self.watermarks.get_mut(id).unwrap(); - watermarks.push_unsubscribe_ack(unsubscribe.pkid); - self.fresh_acks_notification(id); - } - - fn handle_connection_publish(&mut self, id: ConnectionId, publish: Publish) { - let Publish { - pkid, - topic, - payload, - qos, - retain, - .. - } = publish; - - let is_new_topic = if retain { - let is_new_topic = match self.datalog.retain(&topic, payload) { - Some(v) => v, - None => return, - }; - - if qos as u8 > 0 { - let watermarks = self.watermarks.get_mut(id).unwrap(); - watermarks.push_publish_ack(pkid, qos as u8); - } - - is_new_topic - } else { - if payload.is_empty() { - warn!("Empty publish. ID = {:?}, topic = {:?}", id, topic); - // Some tests in paho test suite are sending empty publishes. - // Disabling this filter for the time being - // return; - } - - let (is_new_topic, _) = match self.datalog.append(&topic, payload) { - Some(v) => v, - None => return, - }; - - if qos as u8 > 0 { - let watermarks = self.watermarks.get_mut(id).unwrap(); - watermarks.push_publish_ack(pkid, qos as u8); - } - - is_new_topic - }; - - // If there is a new unique append, send it to connection waiting on it - // This is equivalent to hybrid of block and poll and we don't need timers. - // Connections/Replicator will make a request and request might fail as - // there are no new topic publishes. Router caches the failed request on the topic. - // If there is new topic, router fulfills the last failed request. - // Note that the above commitlog append distinguishes `is_new_topic` by connection's - // commitlog (i.e native or replica commitlog). So there is a chance that topic log - // has duplicate topics. Tracker filters these duplicates though - if is_new_topic { - self.topicslog.append(&topic); - self.fresh_topics_notification(); - } - - // Notify waiters on this topic of new data - self.fresh_data_notification(&topic); - - // Data from topics with replication factor = 0 should be acked immediately if there are - // waiters registered. We shouldn't rely on replication acks for data acks in this case - self.fresh_acks_notification(id); - } - - /// Send notifications to links which registered them. Id is only used to - /// identify if new topics are from a replicator. - /// New topic from one connection involves notifying other connections which - /// are possibly interested in these topics. So this involves going through - /// all the topic waiters - fn fresh_topics_notification(&mut self) { - let waiters = &mut self.topics_waiters; - - while let Some((link_id, request)) = waiters.pop_front() { - // Ignore connections with zero subscriptions. - let tracker = self.trackers.get_mut(link_id).unwrap(); - if tracker.subscription_count() == 0 { - // panic!("Topics request registration for 0 subscription connection"); - continue; - } - - trace!("{:11} {:14} Id = {}", "topics", "notification", link_id); - - // Match new topics with existing subscriptions of this link. - // Even though there are new topics, it's possible that they didn't match - // subscriptions held by this connection. - // Register next topics request in the router - tracker.register_topics_request(TopicsRequest::offset(request.offset)); - - // If connection is removed from ready queue because of 0 requests, - // but connection itself is ready for more notifications, add - // connection back to ready queue - if tracker.empty_unschedule() { - self.readyqueue.push_back(link_id); - tracker.set_empty_unschedule(false); - } - } - - waiters.prepare_next(); - } - - /// Send data to links which registered them - fn fresh_data_notification(&mut self, topic: &str) { - // There might not be any waiters on this topic - // FIXME: Every notification trigger is a hashmap lookup - let waiters = match self.data_waiters.get_mut(topic) { - Some(waiters) => waiters, - None => return, - }; - - while let Some((link_id, request)) = waiters.pop_front() { - let tracker = self.trackers.get_mut(link_id).unwrap(); - - let topic = request.topic; - let qos = request.qos; - let cursors = request.cursor; - let last_retain = request.last_retain; - - let request = DataRequest::offsets(topic, qos, cursors, last_retain); - tracker.register_data_request(request); - - // If connection is removed from ready queue because of 0 requests, - // but connection itself is ready for more notifications, add - // connection back to ready queue - if tracker.empty_unschedule() { - self.readyqueue.push_back(link_id); - tracker.set_empty_unschedule(false); - } - } - - waiters.prepare_next(); - } - - fn fresh_acks_notification(&mut self, id: ConnectionId) { - let watermarks = self.watermarks.get_mut(id).unwrap(); - - // Unlike data and topics where notifications are routed - // to other connections, acks are meant for the same connection. - // We use watermark's own flag to determine if it's waiting for - // a notification - if watermarks.take_pending_acks_request().is_some() { - trace!("{:11} {:14} Id = {}", "acks", "notification", id); - - // Add next acks request to the tracker - let tracker = self.trackers.get_mut(id).unwrap(); - tracker.register_acks_request(); - - // If connection is removed from ready queue because of 0 requests, - // but connection itself is ready for more notifications, add - // connection back to ready queue - if tracker.empty_unschedule() { - self.readyqueue.push_back(id); - tracker.set_empty_unschedule(false); - } - } - } -} - -fn handle_data_request( - id: ConnectionId, - request: DataRequest, - datalog: &mut DataLog, - waiters: &mut DataWaiters, -) -> Option { - trace!( - "{:11} {:14} Id = {} Topic = {} Offsets = {:?}", - "data", - "request", - id, - request.topic, - request.cursor - ); - - let data = match datalog.extract_data(&request) { - Some(data) => { - trace!( - "{:11} {:14} Id = {} Topic = {} Offsets = {:?} Count = {}", - "data", - "response", - id, - data.topic, - data.cursor, - data.payload.len() - ); - - data - } - None => { - trace!( - "{:11} {:14} Id = {} Topic = {}", - "data", - "register", - id, - request.topic - ); - - waiters.register(id, request); - return None; - } - }; - - Some(data) -} - -fn handle_topics_request<'a>( - id: ConnectionId, - request: TopicsRequest, - topicslog: &'a mut TopicsLog, - waiters: &mut TopicsWaiters, -) -> Option> { - trace!( - "{:11} {:14} Id = {} Offset = {}", - "topics", - "request", - id, - request.offset - ); - - let topics = match topicslog.readv(request.offset, request.count) { - Some(topics) => { - trace!( - "{:11} {:14} Id = {} Offset = {} Count = {}", - "topics", - "response", - id, - topics.0, - topics.1.len() - ); - - Topics::new(topics.0, topics.1) - } - None => { - trace!("{:11} {:14} Id = {}", "topics", "register", id); - waiters.register(id, request); - return None; - } - }; - - Some(topics) -} - -fn handle_acks_request(id: ConnectionId, acks: &mut Acks) -> Option> { - trace!("{:11} {:14} Id = {}", "acks", "request", id); - let acks = match acks.handle_acks_request() { - Some(acks) => { - trace!( - "{:11} {:14} Id = {} Count = {}", - "acks", - "response", - id, - acks.len() - ); - - acks - } - None => { - trace!("{:11} {:14} Id = {}", "acks", "register", id); - acks.register_pending_acks_request(); - return None; - } - }; - - Some(acks) -} - -/// Notifies and returns unschedule status if the connection is busy -fn notify(connections: &mut Slab>, id: ConnectionId, reply: Notification) -> bool { - let connection = match connections.get_mut(id) { - Some(c) => c, - None => { - error!("Invalid id while notifying = {:?}", id); - return true; - } - }; - - connection.notify(reply) -} - -#[cfg(test)] -mod test { - use super::*; - use crate::mqttbytes::*; - - #[test] - fn topics_notifications_does_not_create_infinite_loops() { - let (mut router, _tx) = Router::new(Arc::new(Config::default())); - - // Instantiate replica connections with subscriptions and topic request notifications - for i in 10..20 { - let client_id = &format!("{}", i); - add_new_remote_connection(&mut router, client_id); - add_new_subscription(&mut router, i, "hello/world"); - router.topics_waiters.register(i, TopicsRequest::new()); - } - - router.topicslog.append("hello/world"); - - // Next iteration of `fresh_topics_notification` will have all missed notifications - for i in 10..20 { - let request = router.topics_waiters.pop_front().unwrap(); - assert_eq!(i, request.0); - } - } - - #[test] - fn data_notifications_does_not_create_infinite_loops() { - let (mut router, _tx) = Router::new(Arc::new(Config::default())); - - // Instantiate replica connections with subscriptions and topic request notifications - for i in 10..20 { - let client_id = &format!("{}", i); - add_new_remote_connection(&mut router, client_id); - add_new_subscription(&mut router, i, "hello/world"); - router - .data_waiters - .register(i, DataRequest::new("hello/world".to_owned(), 1)); - } - - let payload = Bytes::from(vec![1, 2, 3]); - router.datalog.append("hello/world", payload); - - // Next iteration of `fresh_topics_notification` will have all missed notifications - let waiters = router.data_waiters.get_mut("hello/world").unwrap(); - for i in 10..20 { - let request = waiters.pop_front().unwrap(); - assert_eq!(i, request.0); - } - } - - /// Connection should not be part of next iteration of ready queue - /// when previous notification fails - #[test] - fn failed_notification_connection_should_not_be_scheduled() { - let config = Config { - id: 0, - ..Config::default() - }; - - let (mut router, _tx) = Router::new(Arc::new(config)); - let _rx = add_new_remote_connection(&mut router, "10"); - - // Register 20 data requests in notifications - for i in 0..20 { - let topic = format!("hello/{}/world", i); - let request = DataRequest::new(topic, 1); - router.data_waiters.register(10, request); - } - - // Along with connection ack. 10th notification will fail - for i in 1..=10 { - // Write a publish to commitlog - let publish = Publish::new("hello/world", QoS::AtLeastOnce, vec![1, 2, 3]); - router.handle_connection_data(10, vec![Packet::Publish(publish)]); - - // connack + 8 pub acks. 9 acks before this iteration - // implies 9 notifications + 1 pause notification = 10 notifications - // channel full before this iteration - if i == 9 { - assert!(router.readyqueue.pop_front().is_none()); - break; - } else { - // Trigger 1 request. This will notify puback for above - // data and puts connection back in ready queue. - let id = router.readyqueue.pop_front().unwrap(); - router.connection_ready(id, 1); - } - } - - assert!(router.readyqueue.is_empty()); - } - - fn add_new_remote_connection(router: &mut Router, client_id: &str) -> Receiver { - let (connection, rx) = Connection::new_remote(client_id, true, 10); - router.handle_new_connection(connection); - rx - } - - fn add_new_subscription(router: &mut Router, id: usize, topic: &str) { - router.handle_connection_subscribe(id, Subscribe::new(topic, QoS::AtLeastOnce)); - } -} diff --git a/rumqttd-old/src/rumqttlog/router/slab.rs b/rumqttd-old/src/rumqttlog/router/slab.rs deleted file mode 100644 index aedeb885b..000000000 --- a/rumqttd-old/src/rumqttlog/router/slab.rs +++ /dev/null @@ -1,80 +0,0 @@ -use std::collections::VecDeque; -use std::mem; - -#[derive(Clone, Debug)] -pub struct Slab { - // Chunk of memory - entries: Vec>, - - // Offset of the next available slot in the slab. Set to the slab's - // capacity when the slab is full. - next: VecDeque, -} - -impl Slab { - /// Constructs a new initialized slab - pub fn with_capacity(mut capacity: usize) -> Slab { - // slots for replicators - capacity += 10; - let mut entries = Vec::with_capacity(capacity); - entries.resize_with(capacity, || None::); - - let next: VecDeque = (10..capacity).collect(); - Slab { entries, next } - } - - /// Return a reference to the value associated with the given key. - /// - /// If the given key is not associated with a value, then `None` is - /// returned. - pub fn _get(&self, key: usize) -> Option<&T> { - match self.entries.get(key) { - Some(v) => v.as_ref(), - None => None, - } - } - - /// Return a mutable reference to the value associated with the given key. - /// - /// If the given key is not associated with a value, then `None` is - /// returned. - pub fn get_mut(&mut self, key: usize) -> Option<&mut T> { - match self.entries.get_mut(key) { - Some(v) => v.as_mut(), - None => None, - } - } - - /// Insert a value in the slab, returning key assigned to the value. - /// - /// The returned key can later be used to retrieve or remove the value using indexed - /// lookup and `remove`. Additional capacity is allocated if needed. See - /// [Capacity and reallocation](index.html#capacity-and-reallocation). - pub fn insert(&mut self, val: T) -> Option { - let key = match self.next.pop_front() { - Some(key) => key, - None => return None, - }; - - if let Some(Some(_)) = self.entries.get(key) { - panic!("Inserting in a non vacant space") - }; - - self.entries[key] = Some(val); - Some(key) - } - - pub fn insert_at(&mut self, val: T, at: usize) { - if let Some(Some(_)) = self.entries.get(at) { - panic!("Inserting in a non vacant space") - }; - - self.entries[at] = Some(val); - } - - pub fn remove(&mut self, key: usize) -> Option { - let data = mem::replace(&mut self.entries[key], None); - self.next.push_back(key); - data - } -} diff --git a/rumqttd-old/src/rumqttlog/router/tracker.rs b/rumqttd-old/src/rumqttlog/router/tracker.rs deleted file mode 100644 index fa61c98c6..000000000 --- a/rumqttd-old/src/rumqttlog/router/tracker.rs +++ /dev/null @@ -1,289 +0,0 @@ -use crate::mqttbytes::v4::*; -use crate::mqttbytes::*; -use serde::{Deserialize, Serialize}; -use std::collections::{HashMap, HashSet, VecDeque}; - -use super::{AcksRequest, DataRequest, Request, TopicsRequest}; - -/// Used to register a new connection with the router -/// Connection messages encompasses a handle for router to -/// communicate with this connection -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Tracker { - /// Connection unschedule (from ready queue) status - /// because of request exhaustion - empty_unschedule: bool, - /// Connection unschedule (from ready queue) status - /// due to connection being busy (channel full) - busy_unschedule: bool, - /// Requests to pull data from commitlog - requests: VecDeque, - /// Topics index to not add duplicates to topics - topics_index: HashSet, - /// Concrete subscriptions on this topic - concrete_subscriptions: HashMap, - /// Wildcard subscriptions on this topic - wild_subscriptions: Vec<(String, u8)>, - /// Topics Matches on new subscription waiting for offset updates - matched: VecDeque<(String, u8, (u64, u64))>, -} - -impl Tracker {} - -impl Tracker { - pub fn new() -> Tracker { - let mut requests = VecDeque::with_capacity(100); - requests.push_back(Request::Acks(AcksRequest)); - - Tracker { - empty_unschedule: false, - busy_unschedule: false, - requests, - topics_index: HashSet::new(), - concrete_subscriptions: HashMap::new(), - wild_subscriptions: Vec::new(), - matched: VecDeque::with_capacity(100), - } - } - - pub fn busy_unschedule(&self) -> bool { - self.busy_unschedule - } - - pub fn empty_unschedule(&self) -> bool { - self.empty_unschedule - } - - pub fn set_busy_unschedule(&mut self, b: bool) { - self.busy_unschedule = b; - } - - pub fn set_empty_unschedule(&mut self, b: bool) { - self.empty_unschedule = b; - } - - /// Returns current number of subscriptions - pub fn subscription_count(&self) -> usize { - self.concrete_subscriptions.len() + self.wild_subscriptions.len() - } - - pub fn pop_request(&mut self) -> Option { - self.requests.pop_front() - } - - pub fn register_data_request(&mut self, request: DataRequest) { - let request = Request::Data(request); - self.requests.push_back(request); - } - - pub fn register_topics_request(&mut self, request: TopicsRequest) { - // let request = TopicsRequest::offset(next_offset); - let request = Request::Topics(request); - self.requests.push_back(request); - } - - pub fn register_acks_request(&mut self) { - let request = Request::Acks(AcksRequest); - self.requests.push_back(request); - } - - /// Match and add this topic to requests if it matches. - /// Register new topics - pub fn track_matched_topics(&mut self, topics: &[String]) -> usize { - let mut matched_count = 0; - for topic in topics { - if let Some(request) = self.match_with_subscriptions(topic) { - self.register_data_request(request); - matched_count += 1; - } - } - - matched_count - } - - /// Updates offsets and moves matches to the tracker - pub fn next_matched(&mut self) -> Option<(String, u8, (u64, u64))> { - self.matched.pop_front() - } - - /// A new subscription should match all the existing topics. Tracker - /// should track matched topics from current offset of that topic - /// Adding and matching is combined so that only new subscriptions are - /// matched against provided topics and then added to subscriptions - pub fn add_subscription_and_match( - &mut self, - filters: Vec, - topics: &[String], - ) -> bool { - // Register topics request during first subscription - let mut first = false; - if self.subscription_count() == 0 { - first = true; - } - - for filter in filters { - let subscription = filter.path.clone(); - let qos = filter.qos as u8; - - if has_wildcards(&filter.path) { - self.wild_subscriptions.push((subscription, qos)); - } else { - self.concrete_subscriptions.insert(subscription, qos); - } - - // Check and track matching topics from input - for topic in topics.iter() { - // ignore if the topic is already being tracked - if self.topics_index.contains(topic) { - continue; - } - - if matches(topic, &filter.path) { - self.topics_index.insert(topic.clone()); - let qos = filter.qos as u8; - self.matched.push_back((topic.clone(), qos, (0, 0))); - continue; - } - } - } - - first - } - - /// Matches topic against existing subscriptions. These - /// topics should be tracked by tracker from offset 0. - /// Returns true if this topic matches a subscription for - /// router to trigger new topic notification - fn match_with_subscriptions(&mut self, topic: &str) -> Option { - // ignore if the topic is already being tracked - if self.topics_index.contains(topic) { - return None; - } - - // A concrete subscription match - if let Some(qos) = self.concrete_subscriptions.get(topic) { - self.topics_index.insert(topic.to_owned()); - let request = DataRequest::offsets(topic.to_owned(), *qos, (0, 0), 0); - return Some(request); - } - - // Wildcard subscription match. We return after first match - for (filter, qos) in self.wild_subscriptions.iter() { - if matches(topic, filter) { - self.topics_index.insert(topic.to_owned()); - let request = DataRequest::offsets(topic.to_owned(), *qos, (0, 0), 0); - return Some(request); - } - } - - None - } - - /// Removes a subscription and removes matched topics in tracker - pub fn remove_subscription_and_unmatch(&mut self, filters: Vec) -> VecDeque { - let mut matching = VecDeque::new(); - - // Remove subscriptions and collect topics that match filters - for filter in filters.iter() { - // Collect topics matching current filter - for topic in self.topics_index.iter() { - if matches(topic, filter) { - matching.push_back(topic.clone()); - } - } - - // Remove the subscription - if has_wildcards(filter) { - if let Some(index) = self.wild_subscriptions.iter().position(|v| v.0 == *filter) { - self.wild_subscriptions.swap_remove(index); - } - } else { - self.concrete_subscriptions.remove(filter); - } - } - - let mut pending = VecDeque::new(); - while let Some(topic) = matching.pop_front() { - // Remove this tracked topic from index - self.topics_index.remove(&topic); - - // Find the topic in request queue - let position = self - .requests - .iter() - .position(|request| matches!(request, Request::Data(data) if data.topic == topic)); - - match position { - Some(i) => { - self.requests.swap_remove_back(i); - } - None => pending.push_back(topic), - } - } - - pending - } -} - -impl Default for Tracker { - fn default() -> Self { - Self::new() - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn unsubscribe_removes_requests_from_queue() { - let mut tracker = Tracker::new(); - - let topics = vec!["a/b".to_owned(), "c/d".to_owned(), "e".to_owned()]; - let filter = vec![ - SubscribeFilter::new("+/+".to_owned(), QoS::AtLeastOnce), - SubscribeFilter::new("+".to_owned(), QoS::AtLeastOnce), - ]; - - tracker.add_subscription_and_match(filter, topics.as_slice()); - - while let Some((topic, qos, cursors)) = tracker.next_matched() { - tracker.register_data_request(DataRequest::offsets(topic, qos, cursors, 0)); - } - - let mut t = tracker.requests.iter(); - - let v = t.next().unwrap(); - assert_eq!(*v, Request::Acks(AcksRequest::new())); - - let v = t.next().unwrap(); - assert_eq!(*v, Request::Data(DataRequest::new("a/b".to_owned(), 1))); - assert!(tracker.topics_index.contains("a/b")); - - let v = t.next().unwrap(); - assert_eq!(*v, Request::Data(DataRequest::new("c/d".to_owned(), 1))); - assert!(tracker.topics_index.contains("c/d")); - - let v = t.next().unwrap(); - assert_eq!(*v, Request::Data(DataRequest::new("e".to_owned(), 1))); - assert!(tracker.topics_index.contains("e")); - - // Remove an inflight request - tracker.requests.swap_remove_front(1); - - let o = tracker.remove_subscription_and_unmatch(vec!["+/+".to_owned()]); - assert_eq!(o, vec!["a/b".to_owned()]); - - let mut t = tracker.requests.iter(); - - let v = t.next().unwrap(); - assert_eq!(*v, Request::Acks(AcksRequest::new())); - - let v = t.next().unwrap(); - assert_eq!(*v, Request::Data(DataRequest::new("e".to_owned(), 1))); - - assert!(tracker.topics_index.contains("e")); - assert!(!tracker.topics_index.contains("a/b")); - assert!(!tracker.topics_index.contains("c/d")); - } -} diff --git a/rumqttd-old/src/rumqttlog/waiters/mod.rs b/rumqttd-old/src/rumqttlog/waiters/mod.rs deleted file mode 100644 index 7b26cd58b..000000000 --- a/rumqttd-old/src/rumqttlog/waiters/mod.rs +++ /dev/null @@ -1,111 +0,0 @@ -use std::collections::{HashMap, VecDeque}; - -use super::{router::TopicsRequest, DataRequest}; - -type Topic = String; -type ConnectionId = usize; - -/// Waiter on a topic. These are used to wake connections/replicators -/// which are caught up previously and waiting on new data. -pub struct DataWaiters { - // Map[topic]List[Connections Ids] - waiters: HashMap>, -} - -impl DataWaiters { - pub fn new() -> DataWaiters { - DataWaiters { - waiters: HashMap::new(), - } - } - - pub fn get_mut(&mut self, topic: &str) -> Option<&mut Waiters> { - self.waiters.get_mut(topic) - } - - /// Register data waiter - pub fn register(&mut self, id: ConnectionId, request: DataRequest) { - let topic = request.topic.clone(); - let waiters = self.waiters.entry(topic).or_default(); - waiters.register(id, request); - } - - /// Remove a connection from waiters - pub fn remove(&mut self, id: ConnectionId) -> VecDeque { - let mut pending = VecDeque::new(); - for waiters in self.waiters.values_mut() { - if let Some(request) = waiters.remove(id) { - pending.push_back(request) - } - } - - pending - } -} - -impl Default for DataWaiters { - fn default() -> Self { - Self::new() - } -} - -pub type TopicsWaiters = Waiters; - -/// TopicsWaiters are connections which are waiting to be notified -/// of new topics. Notifications can sometimes be false positives for topics -/// a connection (e.g replicator connection and new topic due to replicator -/// data). This connection should be polled again for next notification. -/// Having 2 waiters to prevents infinite waiter loops while trying to -/// add connection back to waiter queue -#[derive(Debug)] -pub struct Waiters { - /// Waiters on new topics - current: VecDeque<(ConnectionId, T)>, - /// Waiters for next iteration - next: VecDeque<(ConnectionId, T)>, -} - -impl Waiters { - pub fn new() -> Waiters { - Waiters { - current: VecDeque::with_capacity(1000), - next: VecDeque::with_capacity(1000), - } - } - - /// Pushes a request to current wait queue - pub fn register(&mut self, id: ConnectionId, request: T) { - let request = (id, request); - self.current.push_back(request); - } - - /// Pops a request from current wait queue - pub fn pop_front(&mut self) -> Option<(ConnectionId, T)> { - self.current.pop_front() - } - - /// Pushes a request to next wait queue - pub fn push_back(&mut self, id: ConnectionId, request: T) { - let request = (id, request); - self.next.push_back(request); - } - - /// Swaps next wait queue with current wait queue - pub fn prepare_next(&mut self) { - std::mem::swap(&mut self.current, &mut self.next); - } - - /// Remove a connection from waiters - pub fn remove(&mut self, id: ConnectionId) -> Option { - match self.current.iter().position(|x| x.0 == id) { - Some(index) => self.current.swap_remove_back(index).map(|v| v.1), - None => None, - } - } -} - -impl Default for Waiters { - fn default() -> Self { - Self::new() - } -} diff --git a/rumqttd-old/src/state.rs b/rumqttd-old/src/state.rs deleted file mode 100644 index 1f4080bc9..000000000 --- a/rumqttd-old/src/state.rs +++ /dev/null @@ -1,372 +0,0 @@ -use crate::mqttbytes::v4::*; -use crate::mqttbytes::{self, *}; -use crate::rumqttlog::{Message, Notification}; - -use bytes::{Bytes, BytesMut}; -use log::{debug, error, info}; -use std::mem; -use std::vec::IntoIter; - -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("Received unsolicited ack from the device: {0}")] - Unsolicited(u16), - #[error("Collision with an unacked packet: {0}")] - Serialization(#[from] mqttbytes::Error), - #[error("Collision with an unacked packet")] - Collision, - #[error("Duplicate connect")] - DuplicateConnect, - #[error("Client connack")] - ClientConnAck, - #[error("Client disconnect")] - Disconnect, -} - -#[derive(Debug)] -struct Pending { - topic: String, - qos: QoS, - payload: IntoIter, - collision: Option, -} - -impl Pending { - pub fn empty() -> Pending { - Pending { - topic: "".to_string(), - qos: QoS::AtMostOnce, - payload: vec![].into_iter(), - collision: None, - } - } - - pub fn new(topic: String, qos: QoS, payload: IntoIter) -> Pending { - Pending { - topic, - qos, - payload, - collision: None, - } - } - - pub fn len(&self) -> usize { - let len = self.collision.as_ref().map_or(0, |_| 1); - len + self.payload.len() - } - - pub fn next(&mut self) -> Option { - self.payload.next() - } -} - -/// State of the mqtt connection. -/// Design: Methods will just modify the state of the object without doing any network operations -/// Design: All inflight queues are maintained in a pre initialized vec with index as packet id. -/// This is done for 2 reasons -/// Bad acks or out of order acks aren't O(n) causing cpu spikes -/// Any missing acks from the broker are detected during the next recycled use of packet ids -#[derive(Debug)] -pub struct State { - /// Packet id of the last outgoing packet - last_pkid: u16, - /// Number of outgoing inflight publishes - inflight: u16, - /// Maximum number of allowed inflight - max_inflight: u16, - /// Outgoing QoS 1, 2 publishes which aren't acked yet - outgoing_pub: Vec>, - /// Packet ids of released QoS 2 publishes - outgoing_rel: Vec>, - /// Packet ids on incoming QoS 2 publishes - incoming_pub: Vec>, - /// Pending publishes due to collision - pending: Pending, - /// Collected incoming packets - pub incoming: Vec, - /// Write buffer - write: BytesMut, -} - -impl State { - /// Creates new mqtt state. Same state should be used during a - /// connection for persistent sessions while new state should - /// instantiated for clean sessions - pub fn new(max_inflight: u16) -> Self { - State { - last_pkid: 0, - inflight: 0, - max_inflight, - // index 0 is wasted as 0 is not a valid packet id - outgoing_pub: vec![None; max_inflight as usize + 1], - outgoing_rel: vec![None; max_inflight as usize + 1], - incoming_pub: vec![None; std::u16::MAX as usize + 1], - pending: Pending::empty(), - incoming: Vec::with_capacity(10), - write: BytesMut::with_capacity(10 * 1024), - } - } - - pub fn pause_outgoing(&self) -> bool { - self.inflight > self.max_inflight || self.pending.len() > 0 - } - - pub fn take_incoming(&mut self) -> Vec { - mem::replace(&mut self.incoming, Vec::with_capacity(10)) - } - - pub fn write_mut(&mut self) -> &mut BytesMut { - &mut self.write - } - - /// Returns inflight outgoing packets and clears internal queues - pub fn clean(&mut self) -> Vec { - let mut pending = Vec::new(); - let mut acks = Vec::new(); - - // remove and collect pending releases - for rel in self.outgoing_rel.iter_mut() { - if let Some(pkid) = rel.take() { - let packet = Packet::PubRel(PubRel::new(pkid)); - acks.push(packet); - } - } - - pending.push(Notification::Acks(acks)); - - // remove and collect pending publishes - for publish in self.outgoing_pub.iter_mut() { - if let Some(publish) = publish.take() { - let message = Message::new(publish.topic, publish.qos as u8, publish.payload); - pending.push(Notification::Message(message)); - } - } - - pending - } - - pub fn add_pending(&mut self, topic: String, qos: QoS, data: Vec) { - self.pending = Pending::new(topic, qos, data.into_iter()); - } - - /// Adds next packet identifier to QoS 1 and 2 publish packets. - /// Pending packets collide at the first unacked packet id. - /// Processing pending publishes stops at this point. Eventloop - /// waits for incoming acks to clear `pause_outgoing` flag and - /// process more outgoing packets - pub(crate) fn write_pending(&mut self) -> Result<(), Error> { - while let Some(payload) = self.pending.next() { - let mut publish = Publish::from_bytes(&self.pending.topic, self.pending.qos, payload); - - if publish.qos == QoS::AtMostOnce { - debug!("Publish. Qos 0. Payload size = {:?}", publish.payload.len()); - publish.write(&mut self.write)?; - continue; - }; - - // consider PacketIdentifier(0) as uninitialized packets - if 0 == publish.pkid { - publish.pkid = self.next_pkid(); - }; - - let pkid = publish.pkid as usize; - debug!("Publish. Pkid = {}, Size = {}", pkid, publish.payload.len()); - - // If there is an existing publish at this pkid, this implies - // that client hasn't sent ack packet yet. - match self.outgoing_pub.get(pkid).unwrap() { - Some(_) => { - info!( - "Collision on packet id = {:?}. Inflight = {}", - publish.pkid, self.inflight - ); - self.pending.collision = Some(publish); - return Ok(()); - } - None => { - publish.write(&mut self.write)?; - self.outgoing_pub[pkid] = Some(publish); - self.inflight += 1; - } - } - } - - Ok(()) - } - - pub fn outgoing_ack(&mut self, ack: Packet) -> Result<(), Error> { - match ack { - Packet::PubAck(ack) => ack.write(&mut self.write), - Packet::PubRec(ack) => ack.write(&mut self.write), - // Pending outgoing release given by router. Replay - // pubrec with these pkids. In normal flow, release - // id a response for incoming pubrec - Packet::PubRel(ack) => { - self.handle_incoming_pubrec(&PubRec::new(ack.pkid))?; - Ok(10) - } - Packet::PubComp(ack) => ack.write(&mut self.write), - Packet::SubAck(ack) => ack.write(&mut self.write), - Packet::UnsubAck(ack) => ack.write(&mut self.write), - _ => unimplemented!(), - } - .map_err(Error::Serialization)?; - Ok(()) - } - - pub fn handle_network_data(&mut self, packet: Packet) -> Result { - match packet { - Packet::Connect(_) => return Err(Error::DuplicateConnect), - Packet::ConnAck(_) => return Err(Error::ClientConnAck), - Packet::Publish(publish) => { - let forward = self.handle_incoming_publish(&publish)?; - if forward { - self.incoming.push(Packet::Publish(publish)); - } - } - Packet::Subscribe(subscribe) => { - self.incoming.push(Packet::Subscribe(subscribe)); - } - Packet::Unsubscribe(unsubscribe) => { - self.incoming.push(Packet::Unsubscribe(unsubscribe)); - } - Packet::PubAck(ack) => { - self.handle_incoming_puback(&ack)?; - } - Packet::PubRel(ack) => { - self.handle_incoming_pubrel(&ack)?; - } - Packet::PubRec(ack) => { - self.handle_incoming_pubrec(&ack)?; - } - Packet::PubComp(ack) => { - self.handle_incoming_pubcomp(&ack)?; - } - Packet::PingReq => { - self.handle_incoming_pingreq()?; - } - Packet::Disconnect => return Ok(true), - packet => { - error!("Packet = {:?} not supported yet", packet); - // return Err(Error::UnsupportedPacket(packet)) - } - } - - Ok(false) - } - - /// Filters duplicate qos 2 publishes and returns true if publish should be forwarded - /// to the router. - pub fn handle_incoming_publish(&mut self, publish: &Publish) -> Result { - if publish.qos == QoS::ExactlyOnce { - let pkid = publish.pkid; - - // If publish packet is already recorded before, this is a duplicate - // qos 2 publish which should be filtered here. - if mem::replace(&mut self.incoming_pub[pkid as usize], Some(pkid)).is_some() { - PubRec::new(pkid).write(&mut self.write)?; - return Ok(false); - } - } - - Ok(true) - } - - pub fn handle_incoming_puback(&mut self, puback: &PubAck) -> Result<(), Error> { - match mem::replace(&mut self.outgoing_pub[puback.pkid as usize], None) { - Some(_) => self.inflight -= 1, - None => { - error!("Unsolicited puback packet: {:?}", puback.pkid); - return Err(Error::Unsolicited(puback.pkid)); - } - } - - // Check if there is a collision on this packet id before and write it - self.check_and_resolve_collision(puback.pkid)?; - - // Try writing all the previous pending packets because of collision - self.write_pending()?; - Ok(()) - } - - pub fn handle_incoming_pubrec(&mut self, pubrec: &PubRec) -> Result<(), Error> { - match mem::replace(&mut self.outgoing_pub[pubrec.pkid as usize], None) { - // NOTE: Inflight - 1 for qos2 in comp - Some(_) => { - self.outgoing_rel[pubrec.pkid as usize] = Some(pubrec.pkid); - PubRel::new(pubrec.pkid).write(&mut self.write)?; - Ok(()) - } - None => { - error!("Unsolicited pubrec packet: {:?}", pubrec.pkid); - Err(Error::Unsolicited(pubrec.pkid)) - } - } - } - - pub fn handle_incoming_pubrel(&mut self, pubrel: &PubRel) -> Result<(), Error> { - match mem::replace(&mut self.incoming_pub[pubrel.pkid as usize], None) { - Some(_) => { - let response = PubComp::new(pubrel.pkid); - response.write(&mut self.write)?; - Ok(()) - } - None => { - error!("Unsolicited pubrel packet: {:?}", pubrel.pkid); - Err(Error::Unsolicited(pubrel.pkid)) - } - } - } - - pub fn handle_incoming_pubcomp(&mut self, pubcomp: &PubComp) -> Result<(), Error> { - match mem::replace(&mut self.outgoing_rel[pubcomp.pkid as usize], None) { - Some(_) => { - self.inflight -= 1; - } - None => { - error!("Unsolicited pubcomp packet: {:?}", pubcomp.pkid); - return Err(Error::Unsolicited(pubcomp.pkid)); - } - } - - // Check if there is a collision on this packet id before and write it - self.check_and_resolve_collision(pubcomp.pkid)?; - - // Try writing all the previous pending packets because of collision - self.write_pending()?; - Ok(()) - } - - pub fn handle_incoming_pingreq(&mut self) -> Result<(), Error> { - PingResp.write(&mut self.write)?; - Ok(()) - } - - fn check_and_resolve_collision(&mut self, pkid: u16) -> Result<(), Error> { - if let Some(publish) = &self.pending.collision { - if publish.pkid == pkid { - let publish = self.pending.collision.take().unwrap(); - publish.write(&mut self.write)?; - self.outgoing_pub[pkid as usize] = Some(publish); - self.inflight += 1; - info!( - "Resolving collision on packet id = {:?}. Inflight = {}", - pkid, self.inflight - ); - } - } - - Ok(()) - } - - fn next_pkid(&mut self) -> u16 { - let next_pkid = self.last_pkid + 1; - if next_pkid == self.max_inflight { - self.last_pkid = 0; - return next_pkid; - } - - self.last_pkid = next_pkid; - next_pkid - } -} diff --git a/rumqttd/config.patch b/rumqttd/config.patch deleted file mode 100644 index a3511d001..000000000 --- a/rumqttd/config.patch +++ /dev/null @@ -1,83 +0,0 @@ -diff --git a/../rumqttd-old/config/rumqttd.conf b/demo.toml -index ce704a6..2084199 100644 ---- a/../rumqttd-old/config/rumqttd.conf -+++ b/demo.toml -@@ -1,4 +1,3 @@ --# Broker id. Used to identify local node of the replication mesh - id = 0 - - # A commitlog read will pull full segment. Make sure that a segment isn't -@@ -6,40 +5,57 @@ id = 0 - # latencies of other connection. Not a problem with preempting runtimes - [router] - id = 0 --dir = "/tmp/rumqttd" -+instant_ack = true - max_segment_size = 10240 - max_segment_count = 10 -+max_read_len = 10240 - max_connections = 10001 - - # Configuration of server and connections that it accepts --[servers.1] -+[v4.1] -+name = "v4-1" - listen = "0.0.0.0:1883" - next_connection_delay_ms = 1 -- [servers.1.connections] -- connection_timeout_ms = 5000 -+ [v4.1.connections] -+ connection_timeout_ms = 60000 - max_client_id_len = 256 - throttle_delay_ms = 0 -- max_payload_size = 5120 -- max_inflight_count = 200 -+ max_payload_size = 20480 -+ max_inflight_count = 500 - max_inflight_size = 1024 -+ dynamic_filters = true - --# Configuration of server and connections that it accepts --[servers.2] -+Example configuration for a TLS enabled server -+[v4.2] -+name = "v4-2" - listen = "0.0.0.0:8883" - next_connection_delay_ms = 10 -- # Cert config -- [servers.2.cert] -- cert_path = "tlsfiles/server.cert.pem" -- key_path = "tlsfiles/server.key.pem" -- ca_path = "tlsfiles/ca.cert.pem" -- # Connection parameters -- [servers.2.connections] -- connection_timeout_ms = 5000 -+ # tls config for rustls -+ [v4.2.tls] -+ certpath = "./localhost.cert.pem" -+ keypath = "./localhost.key.pem" -+ capath = "./ca.cert.pem" -+ # settings for all the connections on this server -+ [v4.2.connections] -+ connection_timeout_ms = 60000 -+ throttle_delay_ms = 0 -+ max_payload_size = 20480 -+ max_inflight_count = 100 -+ max_inflight_size = 1024 -+ -+[v5.1] -+name = "v5-1" -+listen = "0.0.0.0:1884" -+next_connection_delay_ms = 1 -+ [v5.1.connections] -+ connection_timeout_ms = 60000 - max_client_id_len = 256 - throttle_delay_ms = 0 - max_payload_size = 5120 - max_inflight_count = 100 - max_inflight_size = 1024 - -+[ws] -+ - [console] - listen = "0.0.0.0:3030" From ad3fe7f4c7c44f84623bb9153614bb1453007f24 Mon Sep 17 00:00:00 2001 From: henil Date: Fri, 9 Dec 2022 18:45:10 +0530 Subject: [PATCH 2/2] update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b35d963d..96f69a72b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ### Unreleased --- +misc +--- +- Remove rumqttd-old in favour of rumqttd (#530) + rumqttc ------- - Add support for native-tls within rumqttc (#501)