diff --git a/.config/nextest.toml b/.config/nextest.toml new file mode 100644 index 0000000000..601660c929 --- /dev/null +++ b/.config/nextest.toml @@ -0,0 +1,3 @@ +[[profile.default.overrides]] +filter = 'test(/zenoh_session_unicast/)' +threads-required = 'num-cpus' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4d3e61c0f7..2b845360d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,24 +43,31 @@ jobs: with: command: fmt args: -- --check + env: + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse - name: Clippy uses: actions-rs/cargo@v1 with: command: clippy args: --all-targets -- -D warnings + env: + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse - name: Clippy unstable uses: actions-rs/cargo@v1 with: command: clippy args: --all-targets --features unstable -- -D warnings + env: + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse - name: Clippy shared-memory + if: ${{ matrix.os == 'ubuntu-latest' || matrix.os == 'macOS-latest' }} uses: actions-rs/cargo@v1 with: command: clippy - args: --all-targets --features shared-memory -- -D warnings + args: --all-targets --features shared-memory --features transport_shm -- -D warnings test: name: Run tests on ${{ matrix.os }} @@ -92,6 +99,17 @@ jobs: command: nextest args: run --exclude zenoh-examples --exclude zenoh-plugin-example --workspace env: + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse + ASYNC_STD_THREAD_COUNT: 4 + + - name: Run tests with SHM + if: ${{ matrix.os == 'ubuntu-latest' }} + uses: actions-rs/cargo@v1 + with: + command: nextest + args: run -F shared-memory -F transport_shm -p zenoh-transport + env: + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse ASYNC_STD_THREAD_COUNT: 4 - name: Run doctests @@ -100,6 +118,7 @@ jobs: command: test args: --doc env: + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse ASYNC_STD_THREAD_COUNT: 4 nostd: @@ -123,3 +142,5 @@ jobs: with: command: check args: --bin nostd_check --target x86_64-unknown-none --manifest-path ci/nostd-check/Cargo.toml + env: + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse diff --git a/.github/workflows/crates_check.sh b/.github/workflows/crates_check.sh index 734ce04383..e8ec76cbce 100755 --- a/.github/workflows/crates_check.sh +++ b/.github/workflows/crates_check.sh @@ -1,7 +1,6 @@ cargo check -p zenoh-result --manifest-path commons/zenoh-result/Cargo.toml && cargo check -p zenoh-core --manifest-path commons/zenoh-core/Cargo.toml && cargo check -p zenoh-keyexpr --manifest-path commons/zenoh-keyexpr/Cargo.toml && -cargo check -p zenoh-cfg-properties --manifest-path commons/zenoh-cfg-properties/Cargo.toml && cargo check -p zenoh-collections --manifest-path commons/zenoh-collections/Cargo.toml && cargo check -p zenoh-crypto --manifest-path commons/zenoh-crypto/Cargo.toml && cargo check -p zenoh-buffers --manifest-path commons/zenoh-buffers/Cargo.toml && diff --git a/.github/workflows/crates_publish.sh b/.github/workflows/crates_publish.sh index 32c31b5bcc..4e8b93b856 100755 --- a/.github/workflows/crates_publish.sh +++ b/.github/workflows/crates_publish.sh @@ -2,7 +2,6 @@ cargo login $1 (cd commons/zenoh-result && cargo publish) (cd commons/zenoh-core && cargo publish) (cd commons/zenoh-keyexpr && cargo publish) -(cd commons/zenoh-cfg-properties && cargo publish) (cd commons/zenoh-collections && cargo publish) (cd commons/zenoh-crypto && cargo publish) (cd commons/zenoh-buffers && cargo publish) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 636fa3e6a2..5b9fde20d7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -48,6 +48,9 @@ jobs: with: command: fmt args: -- --check + env: + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse + - name: Clippy check uses: actions-rs/cargo@v1 with: @@ -116,19 +119,25 @@ jobs: run: rustup show - name: Install nextest run: cargo install cargo-nextest --locked + env: + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse + - name: Run tests uses: actions-rs/cargo@v1 with: command: nextest args: run --release --features=${{ github.event.inputs.features}} --verbose env: + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse ASYNC_STD_THREAD_COUNT: 4 + - name: Run doctests uses: actions-rs/cargo@v1 with: command: test args: --release --features=${{ github.event.inputs.features}} --doc env: + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse ASYNC_STD_THREAD_COUNT: 4 doc: @@ -142,8 +151,6 @@ jobs: - name: Install Rust toolchain nightly for docs gen run: rustup toolchain install nightly - name: generate doc - env: - RUSTDOCFLAGS: -Dwarnings uses: actions-rs/cargo@v1 with: toolchain: nightly @@ -248,6 +255,8 @@ jobs: with: command: deb args: --no-build --target=${{ matrix.job.target }} -p zenohd + env: + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse - name: Debian package - zenoh-plugin-storage-manager if: contains(matrix.job.target, '-linux-gnu') @@ -255,6 +264,8 @@ jobs: with: command: deb args: --no-build --target=${{ matrix.job.target }} -p zenoh-plugin-storage-manager + env: + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse - name: Debian package - zenoh-plugin-rest if: contains(matrix.job.target, '-linux-gnu') @@ -262,6 +273,8 @@ jobs: with: command: deb args: --no-build --target=${{ matrix.job.target }} -p zenoh-plugin-rest + env: + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse - name: Packaging id: package diff --git a/Cargo.lock b/Cargo.lock index 7b0283b56c..cb60b4475e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,6 +38,16 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "advisory-lock" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6caee7d48f930f9ad3fc9546f8cbf843365da0c5b0ca4eee1d1ac3dd12d8f93" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "aead" version = "0.3.2" @@ -802,6 +812,26 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" +[[package]] +name = "const_format" +version = "0.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c990efc7a285731f9a4378d81aff2f0e85a2c8781a05ef0f8baa8dac54d0ff48" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e026b6ce194a874cb9cf32cd5772d1ef9767cc8fcb5765948d74f37a9d8b2bf6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "constant_time_eq" version = "0.1.5" @@ -1133,6 +1163,17 @@ dependencies = [ "serde", ] +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + [[package]] name = "errno" version = "0.3.2" @@ -1195,6 +1236,16 @@ dependencies = [ "web-sys", ] +[[package]] +name = "filepath" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7faa16fcec147281a1719947edb44af4f9124964bf7476bd5f5356a48e44dcc" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "fixedbitset" version = "0.4.2" @@ -2935,7 +2986,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" dependencies = [ "bitflags 1.3.2", - "errno", + "errno 0.3.2", "io-lifetimes", "libc", "linux-raw-sys", @@ -3913,6 +3964,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + [[package]] name = "universal-hash" version = "0.4.0" @@ -3923,6 +3980,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "unix-named-pipe" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ad653da8f36ac5825ba06642b5a3cce14a4e52c6a5fab4a8928d53f4426dae2" +dependencies = [ + "errno 0.2.8", + "libc", +] + [[package]] name = "unsafe-libyaml" version = "0.2.9" @@ -4391,6 +4458,7 @@ dependencies = [ "lazy_static", "log", "ordered-float", + "paste", "petgraph", "rand 0.8.5", "regex", @@ -4403,7 +4471,6 @@ dependencies = [ "uuid", "vec_map", "zenoh-buffers", - "zenoh-cfg-properties", "zenoh-codec", "zenoh-collections", "zenoh-config", @@ -4428,19 +4495,15 @@ dependencies = [ "zenoh-collections", ] -[[package]] -name = "zenoh-cfg-properties" -version = "0.10.0-dev" -dependencies = [ - "zenoh-result", -] - [[package]] name = "zenoh-codec" version = "0.10.0-dev" dependencies = [ "criterion", + "env_logger", + "log", "rand 0.8.5", + "serde", "uhlc", "uuid", "zenoh-buffers", @@ -4463,7 +4526,6 @@ dependencies = [ "serde_json", "serde_yaml", "validated_struct", - "zenoh-cfg-properties", "zenoh-core", "zenoh-protocol", "zenoh-result", @@ -4551,11 +4613,11 @@ dependencies = [ "async-std", "async-trait", "rcgen", - "zenoh-cfg-properties", "zenoh-config", "zenoh-link-commons", "zenoh-link-quic", "zenoh-link-serial", + "zenoh-link-shm", "zenoh-link-tcp", "zenoh-link-tls", "zenoh-link-udp", @@ -4575,7 +4637,6 @@ dependencies = [ "serde", "typenum", "zenoh-buffers", - "zenoh-cfg-properties", "zenoh-codec", "zenoh-protocol", "zenoh-result", @@ -4594,7 +4655,7 @@ dependencies = [ "rustls", "rustls-native-certs", "rustls-pemfile", - "zenoh-cfg-properties", + "rustls-webpki", "zenoh-config", "zenoh-core", "zenoh-link-commons", @@ -4615,7 +4676,6 @@ dependencies = [ "tokio", "uuid", "z-serial", - "zenoh-cfg-properties", "zenoh-collections", "zenoh-config", "zenoh-core", @@ -4626,6 +4686,27 @@ dependencies = [ "zenoh-util", ] +[[package]] +name = "zenoh-link-shm" +version = "0.10.0-dev" +dependencies = [ + "advisory-lock", + "async-io", + "async-std", + "async-trait", + "filepath", + "log", + "nix 0.26.2", + "rand 0.8.5", + "unix-named-pipe", + "zenoh-buffers", + "zenoh-config", + "zenoh-core", + "zenoh-link-commons", + "zenoh-protocol", + "zenoh-result", +] + [[package]] name = "zenoh-link-tcp" version = "0.10.0-dev" @@ -4654,7 +4735,6 @@ dependencies = [ "rustls-pemfile", "rustls-webpki", "webpki-roots", - "zenoh-cfg-properties", "zenoh-config", "zenoh-core", "zenoh-link-commons", @@ -4769,7 +4849,6 @@ dependencies = [ "serde_json", "tide", "zenoh", - "zenoh-cfg-properties", "zenoh-plugin-trait", "zenoh-result", "zenoh-util", @@ -4824,6 +4903,7 @@ dependencies = [ name = "zenoh-protocol" version = "0.10.0-dev" dependencies = [ + "const_format", "hex", "lazy_static", "rand 0.8.5", @@ -4886,8 +4966,8 @@ dependencies = [ "ringbuffer-spsc", "rsa", "serde", + "sha3", "zenoh-buffers", - "zenoh-cfg-properties", "zenoh-codec", "zenoh-collections", "zenoh-config", @@ -4898,6 +4978,7 @@ dependencies = [ "zenoh-result", "zenoh-shm", "zenoh-sync", + "zenoh-transport", "zenoh-util", ] diff --git a/Cargo.toml b/Cargo.toml index 68377af972..3b95999788 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,6 @@ [workspace] members = [ "commons/zenoh-buffers", - "commons/zenoh-cfg-properties", "commons/zenoh-codec", "commons/zenoh-collections", "commons/zenoh-config", @@ -37,6 +36,7 @@ members = [ "io/zenoh-links/zenoh-link-udp/", "io/zenoh-links/zenoh-link-unixsock_stream/", "io/zenoh-links/zenoh-link-ws/", + "io/zenoh-links/zenoh-link-shm/", "io/zenoh-transport", "plugins/example-plugin", "plugins/zenoh-backend-traits", @@ -79,6 +79,7 @@ async-trait = "0.1.60" base64 = "0.21.0" bincode = "1.3.3" clap = "3.2.23" +const_format = "0.2.30" crc = "3.0.1" criterion = "0.4.0" derive_more = "0.99.17" @@ -169,7 +170,6 @@ zenoh-codec = { version = "0.10.0-dev", path = "commons/zenoh-codec" } zenoh-sync = { version = "0.10.0-dev", path = "commons/zenoh-sync" } zenoh-collections = { version = "0.10.0-dev", path = "commons/zenoh-collections", default-features = false } zenoh-macros = { version = "0.10.0-dev", path = "commons/zenoh-macros" } -zenoh-cfg-properties = { version = "0.10.0-dev", path = "commons/zenoh-cfg-properties" } zenoh-plugin-trait = { version = "0.10.0-dev", path = "plugins/zenoh-plugin-trait", default-features = false } zenoh_backend_traits = { version = "0.10.0-dev", path = "plugins/zenoh-backend-traits" } zenoh-transport = { version = "0.10.0-dev", path = "io/zenoh-transport" } @@ -179,6 +179,7 @@ zenoh-link-unixsock_stream = { version = "0.10.0-dev", path = "io/zenoh-links/ze zenoh-link-quic = { version = "0.10.0-dev", path = "io/zenoh-links/zenoh-link-quic" } zenoh-link-udp = { version = "0.10.0-dev", path = "io/zenoh-links/zenoh-link-udp" } zenoh-link-ws = { version = "0.10.0-dev", path = "io/zenoh-links/zenoh-link-ws" } +zenoh-link-shm = { version = "0.10.0-dev", path = "io/zenoh-links/zenoh-link-shm" } zenoh-link-serial = { version = "0.10.0-dev", path = "io/zenoh-links/zenoh-link-serial" } zenoh-link = { version = "0.10.0-dev", path = "io/zenoh-link" } zenoh-link-commons = { version = "0.10.0-dev", path = "io/zenoh-link-commons" } diff --git a/DEFAULT_CONFIG.json5 b/DEFAULT_CONFIG.json5 index e07e16fcac..6728494bb1 100644 --- a/DEFAULT_CONFIG.json5 +++ b/DEFAULT_CONFIG.json5 @@ -130,17 +130,17 @@ enabled: true, }, link: { - // /// An optional whitelist of protocols to be used for accepting and opening sessions. - // /// If not configured, all the supported protocols are automatically whitelisted. - // /// The supported protocols are: ["tcp" , "udp", "tls", "quic", "ws", "unixsock-stream"] - // /// For example, to only enable "tls" and "quic": + /// An optional whitelist of protocols to be used for accepting and opening sessions. + /// If not configured, all the supported protocols are automatically whitelisted. + /// The supported protocols are: ["tcp" , "udp", "tls", "quic", "ws", "unixsock-stream"] + /// For example, to only enable "tls" and "quic": // protocols: ["tls", "quic"], /// Configure the zenoh TX parameters of a link tx: { - /// The largest value allowed for Zenoh message sequence numbers (wrappring to 0 when reached). + /// The resolution in bits to be used for the message sequence numbers. /// When establishing a session with another Zenoh instance, the lowest value of the two instances will be used. - /// Defaults to 2^28. - sequence_number_resolution: 268435456, + /// Accepted values: 8bit, 16bit, 32bit, 64bit. + sequence_number_resolution: "32bit", /// Link lease duration in milliseconds to announce to other zenoh nodes lease: 10000, /// Number of keep-alive messages in a link lease duration. If no data is sent, keep alive @@ -224,7 +224,7 @@ }, /// Shared memory configuration shared_memory: { - enabled: true, + enabled: false, }, /// Access control configuration auth: { diff --git a/commons/zenoh-buffers/Cargo.toml b/commons/zenoh-buffers/Cargo.toml index a2df1767b6..b7a7c87634 100644 --- a/commons/zenoh-buffers/Cargo.toml +++ b/commons/zenoh-buffers/Cargo.toml @@ -26,6 +26,7 @@ description = "Internal crate for zenoh." # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] default = ["std"] +shared-memory = [] std = [] test = ["rand"] diff --git a/commons/zenoh-buffers/src/bbuf.rs b/commons/zenoh-buffers/src/bbuf.rs index 7342193902..bdb9e9a056 100644 --- a/commons/zenoh-buffers/src/bbuf.rs +++ b/commons/zenoh-buffers/src/bbuf.rs @@ -26,6 +26,7 @@ pub struct BBuf { } impl BBuf { + #[must_use] pub fn with_capacity(capacity: usize) -> Self { Self { buffer: vec::uninit(capacity).into_boxed_slice(), @@ -33,24 +34,30 @@ impl BBuf { } } - pub fn capacity(&self) -> usize { + #[must_use] + pub const fn capacity(&self) -> usize { self.buffer.len() } - pub fn len(&self) -> usize { + #[must_use] + pub const fn len(&self) -> usize { self.len } - pub fn is_empty(&self) -> bool { + #[must_use] + pub const fn is_empty(&self) -> bool { self.len == 0 } + #[must_use] pub fn as_slice(&self) -> &[u8] { - &self.buffer[..self.len] + // SAFETY: self.len is ensured by the writer to be smaller than buffer length. + crate::unsafe_slice!(self.buffer, ..self.len) } pub fn as_mut_slice(&mut self) -> &mut [u8] { - &mut self.buffer[..self.len] + // SAFETY: self.len is ensured by the writer to be smaller than buffer length. + crate::unsafe_slice_mut!(self.buffer, ..self.len) } pub fn clear(&mut self) { @@ -58,7 +65,8 @@ impl BBuf { } fn as_writable_slice(&mut self) -> &mut [u8] { - &mut self.buffer[self.len..] + // SAFETY: self.len is ensured by the writer to be smaller than buffer length. + crate::unsafe_slice_mut!(self.buffer, self.len..) } } diff --git a/commons/zenoh-buffers/src/lib.rs b/commons/zenoh-buffers/src/lib.rs index 625e43bb35..718f486def 100644 --- a/commons/zenoh-buffers/src/lib.rs +++ b/commons/zenoh-buffers/src/lib.rs @@ -33,6 +33,46 @@ pub use bbuf::*; pub use zbuf::*; pub use zslice::*; +// SAFETY: this crate operates on eventually initialized slices for read and write. Because of that, internal buffers +// implementation keeps track of various slices indexes. Boundaries checks are performed by individual +// implementations every time they need to access a slices. This means, that accessing a slice with [] +// syntax after having already verified the indexes will force the compiler to verify again the slice +// boundaries. In case of access violation the program will panic. However, it is desirable to avoid redundant +// checks for performance reasons. Nevertheless, it is desirable to keep those checks for testing and debugging +// purposes. Hence, the macros below will allow to switch boundaries check in case of test and to avoid them in +// all the other cases. +#[cfg(any(test, feature = "test"))] +#[macro_export] +macro_rules! unsafe_slice { + ($s:expr,$r:expr) => { + &$s[$r] + }; +} + +#[cfg(any(test, feature = "test"))] +#[macro_export] +macro_rules! unsafe_slice_mut { + ($s:expr,$r:expr) => { + &mut $s[$r] + }; +} + +#[cfg(all(not(test), not(feature = "test")))] +#[macro_export] +macro_rules! unsafe_slice { + ($s:expr,$r:expr) => { + unsafe { $s.get_unchecked($r) } + }; +} + +#[cfg(all(not(test), not(feature = "test")))] +#[macro_export] +macro_rules! unsafe_slice_mut { + ($s:expr,$r:expr) => { + unsafe { $s.get_unchecked_mut($r) } + }; +} + pub mod writer { use crate::ZSlice; use core::num::NonZeroUsize; @@ -87,14 +127,14 @@ pub mod reader { fn read_exact(&mut self, into: &mut [u8]) -> Result<(), DidntRead>; fn remaining(&self) -> usize; - /// Returns an iterator of ZSlices such that the sum of their length is _exactly_ `len`. + /// Returns an iterator of `ZSlices` such that the sum of their length is _exactly_ `len`. fn read_zslices( &mut self, len: usize, for_each_slice: F, ) -> Result<(), DidntRead>; - /// Reads exactly `len` bytes, returning them as a single ZSlice. + /// Reads exactly `len` bytes, returning them as a single `ZSlice`. fn read_zslice(&mut self, len: usize) -> Result; fn read_u8(&mut self) -> Result { @@ -123,7 +163,7 @@ pub mod reader { pub struct DidntSiphon; pub trait SiphonableReader: Reader { - fn siphon(&mut self, writer: W) -> Result + fn siphon(&mut self, writer: &mut W) -> Result where W: crate::writer::Writer; } @@ -145,7 +185,7 @@ pub trait SplitBuffer<'a> { /// Returns `true` if the buffer has a length of 0. fn is_empty(&'a self) -> bool { - self.slices().all(|s| s.is_empty()) + self.slices().all(<[u8]>::is_empty) } /// Returns the number of bytes in the buffer. diff --git a/commons/zenoh-buffers/src/slice.rs b/commons/zenoh-buffers/src/slice.rs index 62fbf99c4a..6056bb9606 100644 --- a/commons/zenoh-buffers/src/slice.rs +++ b/commons/zenoh-buffers/src/slice.rs @@ -34,12 +34,20 @@ impl Writer for &mut [u8] { return Err(DidntWrite); } - self[..len].copy_from_slice(&bytes[..len]); - // Safety: this doesn't compile with simple assignment because the compiler + // SAFETY: len is guaranteed to be the minimum between lhs and rhs length. + // We early return if length is 0. + let lhs = crate::unsafe_slice_mut!(self, ..len); + let rhs = crate::unsafe_slice!(bytes, ..len); + lhs.copy_from_slice(rhs); + + // SAFETY: len is guaranteed to be the minimum between lhs and rhs length. + let lhs = crate::unsafe_slice_mut!(self, len..); + // SAFETY: this doesn't compile with simple assignment because the compiler // doesn't believe that the subslice has the same lifetime as the original slice, // so we transmute to assure it that it does. - *self = unsafe { mem::transmute(&mut self[len..]) }; - // Safety: this operation is safe since we check if len is non-zero + *self = unsafe { mem::transmute(lhs) }; + + // SAFETY: this operation is safe since we check if len is non-zero. Ok(unsafe { NonZeroUsize::new_unchecked(len) }) } @@ -49,11 +57,18 @@ impl Writer for &mut [u8] { return Err(DidntWrite); } - self[..len].copy_from_slice(&bytes[..len]); - // Safety: this doesn't compile with simple assignment because the compiler + // SAFETY: len is guaranteed to be the smaller than lhs length. + let lhs = crate::unsafe_slice_mut!(self, ..len); + let rhs = crate::unsafe_slice!(bytes, ..len); + lhs.copy_from_slice(rhs); + + // SAFETY: len is guaranteed to be the minimum between lhs and rhs length. + let lhs = crate::unsafe_slice_mut!(self, len..); + // SAFETY: this doesn't compile with simple assignment because the compiler // doesn't believe that the subslice has the same lifetime as the original slice, // so we transmute to assure it that it does. - *self = unsafe { mem::transmute(&mut self[len..]) }; + *self = unsafe { mem::transmute(lhs) }; + Ok(()) } @@ -68,11 +83,15 @@ impl Writer for &mut [u8] { if len > self.len() { return Err(DidntWrite); } - len = f(&mut self[..len]); - // Safety: this doesn't compile with simple assignment because the compiler + // SAFETY: we early return in case len is greater than slice.len(). + let s = crate::unsafe_slice_mut!(self, ..len); + len = f(s); + // SAFETY: we early return in case len is greater than slice.len(). + let s = crate::unsafe_slice_mut!(self, len..); + // SAFETY: this doesn't compile with simple assignment because the compiler // doesn't believe that the subslice has the same lifetime as the original slice, // so we transmute to assure it that it does. - *self = unsafe { mem::transmute(&mut self[len..]) }; + *self = unsafe { mem::transmute(s) }; NonZeroUsize::new(len).ok_or(DidntWrite) } @@ -97,7 +116,7 @@ impl<'s> BacktrackableWriter for &'s mut [u8] { } fn rewind(&mut self, mark: Self::Mark) -> bool { - // Safety: SliceMark's lifetime is bound to the slice's lifetime + // SAFETY: SliceMark's lifetime is bound to the slice's lifetime *self = unsafe { slice::from_raw_parts_mut(mark.ptr as *mut u8, mark.len) }; true } @@ -115,8 +134,12 @@ impl<'a> HasReader for &'a [u8] { impl Reader for &[u8] { fn read(&mut self, into: &mut [u8]) -> Result { let len = self.len().min(into.len()); - into[..len].copy_from_slice(&self[..len]); - *self = &self[len..]; + // SAFETY: len is guaranteed to be the smaller than lhs length. + let lhs = crate::unsafe_slice_mut!(into, ..len); + let rhs = crate::unsafe_slice!(self, ..len); + lhs.copy_from_slice(rhs); + // SAFETY: len is guaranteed to be smaller than slice.len(). + *self = crate::unsafe_slice!(self, len..); NonZeroUsize::new(len).ok_or(DidntRead) } @@ -125,9 +148,12 @@ impl Reader for &[u8] { if self.len() < len { return Err(DidntRead); } - - into.copy_from_slice(&self[..len]); - *self = &self[len..]; + // SAFETY: len is guaranteed to be the smaller than lhs length. + let lhs = crate::unsafe_slice_mut!(into, ..len); + let rhs = crate::unsafe_slice!(self, ..len); + lhs.copy_from_slice(rhs); + // SAFETY: len is guaranteed to be smaller than slice.len(). + *self = crate::unsafe_slice!(self, len..); Ok(()) } @@ -135,8 +161,10 @@ impl Reader for &[u8] { if !self.can_read() { return Err(DidntRead); } - let ret = self[0]; - *self = &self[1..]; + // SAFETY: we early return in case the slice is empty. + // Therefore, there is at least one element in the slice. + let ret = *crate::unsafe_slice!(self, 0); + *self = crate::unsafe_slice!(self, 1..); Ok(ret) } @@ -147,7 +175,7 @@ impl Reader for &[u8] { } fn read_zslice(&mut self, len: usize) -> Result { - // Safety: the buffer is initialized by the `read_exact()` function. Should the `read_exact()` + // SAFETY: the buffer is initialized by the `read_exact()` function. Should the `read_exact()` // function fail, the `read_zslice()` will fail as well and return None. It is hence guaranteed // that any `ZSlice` returned by `read_zslice()` points to a fully initialized buffer. let mut buffer = crate::vec::uninit(len); @@ -178,13 +206,15 @@ impl<'a> BacktrackableReader for &'a [u8] { } impl<'a> SiphonableReader for &'a [u8] { - fn siphon(&mut self, mut writer: W) -> Result + fn siphon(&mut self, writer: &mut W) -> Result where W: Writer, { let res = writer.write(self).map_err(|_| DidntSiphon); if let Ok(len) = res { - *self = &self[len.get()..]; + // SAFETY: len is returned from the writer, therefore it means + // len amount of bytes have been written to the slice. + *self = crate::unsafe_slice!(self, len.get()..); } res } diff --git a/commons/zenoh-buffers/src/vec.rs b/commons/zenoh-buffers/src/vec.rs index 06774a7340..cbe1ee5801 100644 --- a/commons/zenoh-buffers/src/vec.rs +++ b/commons/zenoh-buffers/src/vec.rs @@ -19,8 +19,10 @@ use alloc::vec::Vec; use core::{mem, num::NonZeroUsize}; /// Allocate a vector with a given capacity and sets the length to that capacity. +#[must_use] pub fn uninit(capacity: usize) -> Vec { let mut vbuf = Vec::with_capacity(capacity); + // SAFETY: this operation is safe since we are setting the length equal to the allocated capacity. #[allow(clippy::uninit_vec)] unsafe { vbuf.set_len(capacity); @@ -43,7 +45,7 @@ impl Writer for &mut Vec { return Err(DidntWrite); } self.extend_from_slice(bytes); - // Safety: this operation is safe since we early return if bytes is empty + // SAFETY: this operation is safe since we early return in case bytes is empty Ok(unsafe { NonZeroUsize::new_unchecked(bytes.len()) }) } @@ -65,10 +67,15 @@ impl Writer for &mut Vec { F: FnOnce(&mut [u8]) -> usize, { self.reserve(len); - unsafe { - len = f(mem::transmute(&mut self.spare_capacity_mut()[..len])); - self.set_len(self.len() + len); - } + + // SAFETY: we already reserved len elements on the vector. + let s = crate::unsafe_slice_mut!(self.spare_capacity_mut(), ..len); + // SAFETY: converting MaybeUninit into [u8] is safe because we are going to write on it. + // The returned len tells us how many bytes have been written so as to update the len accordingly. + len = unsafe { f(&mut *(s as *mut [mem::MaybeUninit] as *mut [u8])) }; + // SAFETY: we already reserved len elements on the vector. + unsafe { self.set_len(self.len() + len) }; + NonZeroUsize::new(len).ok_or(DidntWrite) } } diff --git a/commons/zenoh-buffers/src/zbuf.rs b/commons/zenoh-buffers/src/zbuf.rs index 1f6e29eecc..3f941f48e3 100644 --- a/commons/zenoh-buffers/src/zbuf.rs +++ b/commons/zenoh-buffers/src/zbuf.rs @@ -11,13 +11,15 @@ // Contributors: // ZettaScale Zenoh Team, // +#[cfg(feature = "shared-memory")] +use crate::ZSliceKind; use crate::{ reader::{BacktrackableReader, DidntRead, DidntSiphon, HasReader, Reader, SiphonableReader}, writer::{BacktrackableWriter, DidntWrite, HasWriter, Writer}, - SplitBuffer, ZSlice, ZSliceBuffer, + SplitBuffer, ZSlice, }; use alloc::{sync::Arc, vec::Vec}; -use core::{cmp, iter, mem, num::NonZeroUsize, slice}; +use core::{cmp, iter, mem, num::NonZeroUsize, ptr, slice}; use zenoh_collections::SingleOrVec; fn get_mut_unchecked(arc: &mut Arc) -> &mut T { @@ -30,6 +32,11 @@ pub struct ZBuf { } impl ZBuf { + #[must_use] + pub fn empty() -> Self { + Self::default() + } + pub fn clear(&mut self) { self.slices.clear(); } @@ -43,7 +50,9 @@ impl ZBuf { } pub fn push_zslice(&mut self, zslice: ZSlice) { - self.slices.push(zslice); + if !zslice.is_empty() { + self.slices.push(zslice); + } } } @@ -80,18 +89,25 @@ impl PartialEq for ZBuf { (None, _) | (_, None) => return false, (Some(l), Some(r)) => { let cmp_len = l.len().min(r.len()); - if l[..cmp_len] != r[..cmp_len] { + // SAFETY: cmp_len is the minimum lenght between l and r slices. + let lhs = crate::unsafe_slice!(l, ..cmp_len); + let rhs = crate::unsafe_slice!(r, ..cmp_len); + if lhs != rhs { return false; } if cmp_len == l.len() { - current_self = self_slices.next() + current_self = self_slices.next(); } else { - current_self = Some(&l[cmp_len..]) + // SAFETY: cmp_len is the minimum lenght between l and r slices. + let lhs = crate::unsafe_slice!(l, cmp_len..); + current_self = Some(lhs); } if cmp_len == r.len() { - current_other = other_slices.next() + current_other = other_slices.next(); } else { - current_other = Some(&r[cmp_len..]) + // SAFETY: cmp_len is the minimum lenght between l and r slices. + let rhs = crate::unsafe_slice!(r, cmp_len..); + current_other = Some(rhs); } } } @@ -100,27 +116,17 @@ impl PartialEq for ZBuf { } // From impls -impl From> for ZBuf -where - T: ZSliceBuffer + 'static, -{ - fn from(buf: Arc) -> Self { - let zs: ZSlice = buf.into(); - let mut zbuf = ZBuf::default(); - zbuf.push_zslice(zs); - zbuf - } -} - impl From for ZBuf where - T: ZSliceBuffer + 'static, + T: Into, { - fn from(buf: T) -> Self { - Self::from(Arc::new(buf)) + fn from(t: T) -> Self { + let mut zbuf = ZBuf::empty(); + let zslice: ZSlice = t.into(); + zbuf.push_zslice(zslice); + zbuf } } - // Reader #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct ZBufPos { @@ -150,13 +156,18 @@ impl<'a> Reader for ZBufReader<'a> { let mut read = 0; while let Some(slice) = self.inner.slices.get(self.cursor.slice) { // Subslice from the current read slice - let from = &slice.as_slice()[self.cursor.byte..]; + // SAFETY: validity of self.cursor.byte is ensured by the read logic. + let from = crate::unsafe_slice!(slice.as_slice(), self.cursor.byte..); // Take the minimum length among read and write slices let len = from.len().min(into.len()); // Copy the slice content - into[..len].copy_from_slice(&from[..len]); + // SAFETY: len is the minimum lenght between from and into slices. + let lhs = crate::unsafe_slice_mut!(into, ..len); + let rhs = crate::unsafe_slice!(from, ..len); + lhs.copy_from_slice(rhs); // Advance the write slice - into = &mut into[len..]; + // SAFETY: len is the minimum lenght between from and into slices. + into = crate::unsafe_slice_mut!(into, len..); // Update the counter read += len; // Move the byte cursor @@ -196,10 +207,9 @@ impl<'a> Reader for ZBufReader<'a> { } fn remaining(&self) -> usize { - self.inner.slices.as_ref()[self.cursor.slice..] - .iter() - .fold(0, |acc, it| acc + it.len()) - - self.cursor.byte + // SAFETY: self.cursor.slice validity is ensured by the reader + let s = crate::unsafe_slice!(self.inner.slices.as_ref(), self.cursor.slice..); + s.iter().fold(0, |acc, it| acc + it.len()) - self.cursor.byte } fn read_zslices(&mut self, len: usize, mut f: F) -> Result<(), DidntRead> { @@ -228,7 +238,7 @@ impl<'a> Reader for ZBufReader<'a> { } cmp::Ordering::Equal => { let s = slice - .new_sub_slice(self.cursor.byte, slice.len()) + .subslice(self.cursor.byte, slice.len()) .ok_or(DidntRead)?; self.cursor.slice += 1; @@ -238,9 +248,7 @@ impl<'a> Reader for ZBufReader<'a> { cmp::Ordering::Greater => { let start = self.cursor.byte; self.cursor.byte += len; - slice - .new_sub_slice(start, self.cursor.byte) - .ok_or(DidntRead) + slice.subslice(start, self.cursor.byte).ok_or(DidntRead) } } } @@ -264,14 +272,15 @@ impl<'a> BacktrackableReader for ZBufReader<'a> { } impl<'a> SiphonableReader for ZBufReader<'a> { - fn siphon(&mut self, mut writer: W) -> Result + fn siphon(&mut self, writer: &mut W) -> Result where W: Writer, { let mut read = 0; while let Some(slice) = self.inner.slices.get(self.cursor.slice) { // Subslice from the current read slice - let from = &slice.as_slice()[self.cursor.byte..]; + // SAFETY: self.cursor.byte is ensured by the reader. + let from = crate::unsafe_slice!(slice.as_slice(), self.cursor.byte..); // Copy the slice content match writer.write(from) { Ok(len) => { @@ -321,29 +330,32 @@ impl Iterator for ZBufSliceIterator<'_, '_> { return None; } - let slice = &self.reader.inner.slices[self.reader.cursor.slice]; + // SAFETY: self.reader.cursor.slice is ensured by the reader. + let slice = + crate::unsafe_slice!(self.reader.inner.slices.as_ref(), self.reader.cursor.slice); let start = self.reader.cursor.byte; - let current = &slice[start..]; + // SAFETY: self.reader.cursor.byte is ensured by the reader. + let current = crate::unsafe_slice!(slice, start..); let len = current.len(); match self.remaining.cmp(&len) { - core::cmp::Ordering::Less => { + cmp::Ordering::Less => { let end = start + self.remaining; - let slice = slice.new_sub_slice(start, end); + let slice = slice.subslice(start, end); self.reader.cursor.byte = end; self.remaining = 0; slice } - core::cmp::Ordering::Equal => { + cmp::Ordering::Equal => { let end = start + self.remaining; - let slice = slice.new_sub_slice(start, end); + let slice = slice.subslice(start, end); self.reader.cursor.slice += 1; self.reader.cursor.byte = 0; self.remaining = 0; slice } - core::cmp::Ordering::Greater => { + cmp::Ordering::Greater => { let end = start + len; - let slice = slice.new_sub_slice(start, end); + let slice = slice.subslice(start, end); self.reader.cursor.slice += 1; self.reader.cursor.byte = 0; self.remaining -= len; @@ -381,7 +393,7 @@ impl Writer for ZBufWriter<'_> { return Err(DidntWrite); } self.write_exact(bytes)?; - // Safety: this operation is safe since we check if bytes is empty + // SAFETY: this operation is safe since we check if bytes is empty Ok(unsafe { NonZeroUsize::new_unchecked(bytes.len()) }) } @@ -414,6 +426,8 @@ impl Writer for ZBufWriter<'_> { buf: self.cache.clone(), start: prev_cache_len, end: cache_len, + #[cfg(feature = "shared-memory")] + kind: ZSliceKind::Raw, }); Ok(()) } @@ -438,10 +452,15 @@ impl Writer for ZBufWriter<'_> { let cache = get_mut_unchecked(&mut self.cache); let prev_cache_len = cache.len(); cache.reserve(len); - unsafe { - len = f(mem::transmute(&mut cache.spare_capacity_mut()[..len])); - cache.set_len(prev_cache_len + len); - } + + // SAFETY: we already reserved len elements on the vector. + let s = crate::unsafe_slice_mut!(cache.spare_capacity_mut(), ..len); + // SAFETY: converting MaybeUninit into [u8] is safe because we are going to write on it. + // The returned len tells us how many bytes have been written so as to update the len accordingly. + len = unsafe { f(&mut *(s as *mut [mem::MaybeUninit] as *mut [u8])) }; + // SAFETY: we already reserved len elements on the vector. + unsafe { cache.set_len(prev_cache_len + len) }; + let cache_len = cache.len(); // Verify we are writing on the cache @@ -454,7 +473,7 @@ impl Writer for ZBufWriter<'_> { // Verify the ZSlice is actually a Vec if let Some(b) = buf.as_any().downcast_ref::>() { // Verify the Vec of the ZSlice is exactly the one from the cache - if core::ptr::eq(cache.as_ptr(), b.as_ptr()) { + if ptr::eq(cache.as_ptr(), b.as_ptr()) { // Simply update the slice length *end = cache_len; return NonZeroUsize::new(len).ok_or(DidntWrite); @@ -467,6 +486,8 @@ impl Writer for ZBufWriter<'_> { buf: self.cache.clone(), start: prev_cache_len, end: cache_len, + #[cfg(feature = "shared-memory")] + kind: ZSliceKind::Raw, }); NonZeroUsize::new(len).ok_or(DidntWrite) } @@ -489,9 +510,9 @@ impl BacktrackableWriter for ZBufWriter<'_> { fn rewind(&mut self, mark: Self::Mark) -> bool { self.inner .slices - .truncate(mark.slice + (mark.byte != 0) as usize); + .truncate(mark.slice + usize::from(mark.byte != 0)); if let Some(slice) = self.inner.slices.last_mut() { - slice.end = mark.byte + slice.end = mark.byte; } true } @@ -517,7 +538,7 @@ impl<'a> std::io::Write for ZBufWriter<'a> { #[cfg(feature = "test")] impl ZBuf { pub fn rand(len: usize) -> Self { - let mut zbuf = ZBuf::default(); + let mut zbuf = ZBuf::empty(); zbuf.push_zslice(ZSlice::rand(len)); zbuf } @@ -530,25 +551,25 @@ mod tests { let slice: ZSlice = [0u8, 1, 2, 3, 4, 5, 6, 7].to_vec().into(); - let mut zbuf1 = ZBuf::default(); - zbuf1.push_zslice(slice.new_sub_slice(0, 4).unwrap()); - zbuf1.push_zslice(slice.new_sub_slice(4, 8).unwrap()); + let mut zbuf1 = ZBuf::empty(); + zbuf1.push_zslice(slice.subslice(0, 4).unwrap()); + zbuf1.push_zslice(slice.subslice(4, 8).unwrap()); - let mut zbuf2 = ZBuf::default(); - zbuf2.push_zslice(slice.new_sub_slice(0, 1).unwrap()); - zbuf2.push_zslice(slice.new_sub_slice(1, 4).unwrap()); - zbuf2.push_zslice(slice.new_sub_slice(4, 8).unwrap()); + let mut zbuf2 = ZBuf::empty(); + zbuf2.push_zslice(slice.subslice(0, 1).unwrap()); + zbuf2.push_zslice(slice.subslice(1, 4).unwrap()); + zbuf2.push_zslice(slice.subslice(4, 8).unwrap()); assert_eq!(zbuf1, zbuf2); - let mut zbuf1 = ZBuf::default(); - zbuf1.push_zslice(slice.new_sub_slice(2, 4).unwrap()); - zbuf1.push_zslice(slice.new_sub_slice(4, 8).unwrap()); + let mut zbuf1 = ZBuf::empty(); + zbuf1.push_zslice(slice.subslice(2, 4).unwrap()); + zbuf1.push_zslice(slice.subslice(4, 8).unwrap()); - let mut zbuf2 = ZBuf::default(); - zbuf2.push_zslice(slice.new_sub_slice(2, 3).unwrap()); - zbuf2.push_zslice(slice.new_sub_slice(3, 6).unwrap()); - zbuf2.push_zslice(slice.new_sub_slice(6, 8).unwrap()); + let mut zbuf2 = ZBuf::empty(); + zbuf2.push_zslice(slice.subslice(2, 3).unwrap()); + zbuf2.push_zslice(slice.subslice(3, 6).unwrap()); + zbuf2.push_zslice(slice.subslice(6, 8).unwrap()); assert_eq!(zbuf1, zbuf2); } diff --git a/commons/zenoh-buffers/src/zslice.rs b/commons/zenoh-buffers/src/zslice.rs index 92d016ef0c..294092e682 100644 --- a/commons/zenoh-buffers/src/zslice.rs +++ b/commons/zenoh-buffers/src/zslice.rs @@ -69,12 +69,22 @@ impl ZSliceBuffer for [u8; N] { /*************************************/ /* ZSLICE */ /*************************************/ +#[cfg(feature = "shared-memory")] +#[derive(Copy, Clone, PartialEq, Eq)] +#[repr(u8)] +pub enum ZSliceKind { + Raw = 0, + ShmPtr = 1, +} + /// A clonable wrapper to a contiguous slice of bytes. #[derive(Clone)] pub struct ZSlice { - pub buf: Arc, + pub(crate) buf: Arc, pub(crate) start: usize, pub(crate) end: usize, + #[cfg(feature = "shared-memory")] + pub kind: ZSliceKind, } impl ZSlice { @@ -83,39 +93,62 @@ impl ZSlice { start: usize, end: usize, ) -> Result> { - if end <= buf.as_slice().len() { - Ok(ZSlice { buf, start, end }) + if start <= end && end <= buf.as_slice().len() { + Ok(ZSlice { + buf, + start, + end, + #[cfg(feature = "shared-memory")] + kind: ZSliceKind::Raw, + }) } else { Err(buf) } } #[inline] - pub fn range(&self) -> Range { + #[must_use] + pub fn downcast_ref(&self) -> Option<&T> + where + T: Any, + { + self.buf.as_any().downcast_ref::() + } + + #[inline] + #[must_use] + pub const fn range(&self) -> Range { self.start..self.end } #[inline] - pub fn len(&self) -> usize { + #[must_use] + pub const fn len(&self) -> usize { self.end - self.start } #[inline] - pub fn is_empty(&self) -> bool { + #[must_use] + pub const fn is_empty(&self) -> bool { self.len() == 0 } #[inline] + #[must_use] pub fn as_slice(&self) -> &[u8] { - &self.buf.as_slice()[self.range()] + // SAFETY: bounds checks are performed at `ZSlice` construction via `make()` or `subslice()`. + crate::unsafe_slice!(self.buf.as_slice(), self.range()) } - pub(crate) fn new_sub_slice(&self, start: usize, end: usize) -> Option { - if end <= self.len() { + #[must_use] + pub fn subslice(&self, start: usize, end: usize) -> Option { + if start <= end && end <= self.len() { Some(ZSlice { buf: self.buf.clone(), start: self.start + start, end: self.start + end, + #[cfg(feature = "shared-memory")] + kind: self.kind, }) } else { None @@ -133,7 +166,7 @@ impl Deref for ZSlice { impl AsRef<[u8]> for ZSlice { fn as_ref(&self) -> &[u8] { - self.deref() + self } } @@ -165,7 +198,7 @@ impl Index for ZSlice { type Output = [u8]; fn index(&self, _range: RangeFull) -> &Self::Output { - self.deref() + self } } @@ -220,7 +253,13 @@ where { fn from(buf: Arc) -> Self { let end = buf.as_slice().len(); - Self { buf, start: 0, end } + Self { + buf, + start: 0, + end, + #[cfg(feature = "shared-memory")] + kind: ZSliceKind::Raw, + } } } @@ -229,12 +268,7 @@ where T: ZSliceBuffer + 'static, { fn from(buf: T) -> Self { - let end = buf.as_slice().len(); - Self { - buf: Arc::new(buf), - start: 0, - end, - } + Self::from(Arc::new(buf)) } } @@ -276,7 +310,7 @@ impl Reader for &mut ZSlice { } fn read_zslice(&mut self, len: usize) -> Result { - let res = self.new_sub_slice(0, len).ok_or(DidntRead)?; + let res = self.subslice(0, len).ok_or(DidntRead)?; self.start += len; Ok(res) } diff --git a/commons/zenoh-buffers/tests/readwrite.rs b/commons/zenoh-buffers/tests/readwrite.rs index fd761315cb..ea48218a85 100644 --- a/commons/zenoh-buffers/tests/readwrite.rs +++ b/commons/zenoh-buffers/tests/readwrite.rs @@ -11,10 +11,11 @@ // Contributors: // ZettaScale Zenoh Team, // -use std::sync::Arc; -use zenoh_buffers::reader::*; -use zenoh_buffers::writer::*; -use zenoh_buffers::*; +use zenoh_buffers::{ + reader::{HasReader, Reader, SiphonableReader}, + writer::{BacktrackableWriter, HasWriter, Writer}, +}; +use zenoh_buffers::{BBuf, ZBuf, ZSlice}; const BYTES: usize = 18; @@ -91,6 +92,18 @@ macro_rules! run_read { }; } +macro_rules! run_empty { + ($buffer:expr) => { + let mut s = [0u8; 64]; + + println!(">>> Read empty"); + let mut reader = $buffer.reader(); + assert!(reader.read_u8().is_err()); + assert!(reader.read(&mut s).is_err()); + assert!(reader.read_exact(&mut s).is_err()); + }; +} + macro_rules! run_bound { ($buffer:expr, $capacity:expr) => { println!(">>> Write bound"); @@ -127,8 +140,8 @@ macro_rules! run_siphon { while read < $fcap { $into.clear(); - let writer = $into.writer(); - let written = reader.siphon(writer).unwrap(); + let mut writer = $into.writer(); + let written = reader.siphon(&mut writer).unwrap(); let mut reader = $into.reader(); for i in read..read + written.get() { @@ -155,6 +168,7 @@ fn buffer_slice() { fn buffer_vec() { println!("Buffer Vec"); let mut vbuf = vec![]; + run_empty!(vbuf); run_write!(&mut vbuf); run_read!(&vbuf); } @@ -164,6 +178,7 @@ fn buffer_bbuf() { println!("Buffer BBuf"); let capacity = 1 + u8::MAX as usize; let mut bbuf = BBuf::with_capacity(capacity); + run_empty!(bbuf); run_write!(bbuf); run_read!(bbuf); @@ -175,9 +190,13 @@ fn buffer_bbuf() { #[test] fn buffer_zbuf() { println!("Buffer ZBuf"); - let mut zbuf = ZBuf::default(); + let mut zbuf = ZBuf::empty(); + run_empty!(zbuf); run_write!(zbuf); run_read!(zbuf); + + let zbuf = ZBuf::from(vec![]); + run_empty!(zbuf); } #[test] @@ -186,8 +205,11 @@ fn buffer_zslice() { let mut vbuf = vec![]; run_write!(&mut vbuf); - let mut zslice = ZSlice::from(Arc::new(vbuf)); + let mut zslice = ZSlice::from(vbuf); run_read!(zslice); + + let mut zslice = ZSlice::from(vec![]); + run_empty!(zslice); } #[test] @@ -200,12 +222,12 @@ fn buffer_siphon() { run_siphon!(bbuf1, capacity, bbuf2, capacity); println!("Buffer Siphon ZBuf({capacity}) -> ZBuf({capacity})"); - let mut zbuf1 = ZBuf::default(); - let mut zbuf2 = ZBuf::default(); + let mut zbuf1 = ZBuf::empty(); + let mut zbuf2 = ZBuf::empty(); run_siphon!(zbuf1, capacity, zbuf2, capacity); println!("Buffer Siphon ZBuf({capacity}) -> BBuf({capacity})"); - let mut zbuf1 = ZBuf::default(); + let mut zbuf1 = ZBuf::empty(); let mut bbuf1 = BBuf::with_capacity(capacity); run_siphon!(zbuf1, capacity, bbuf1, capacity); @@ -216,7 +238,7 @@ fn buffer_siphon() { run_siphon!(bbuf1, capacity, bbuf2, capacity2); println!("Buffer Siphon ZBuf({capacity}) -> BBuf({capacity2})"); - let mut zbuf1 = ZBuf::default(); + let mut zbuf1 = ZBuf::empty(); let mut bbuf1 = BBuf::with_capacity(capacity2); run_siphon!(zbuf1, capacity, bbuf1, capacity2); } diff --git a/commons/zenoh-cfg-properties/README.md b/commons/zenoh-cfg-properties/README.md deleted file mode 100644 index 45d679d0e8..0000000000 --- a/commons/zenoh-cfg-properties/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# ⚠️ WARNING ⚠️ - -This crate is intended for Zenoh's internal use. -This crate is depecrated and it will be completely removed in the future. - -- [Click here for Zenoh's main repository](https://github.com/eclipse-zenoh/zenoh) -- [Click here for Zenoh's documentation](https://zenoh.io) - - diff --git a/commons/zenoh-cfg-properties/src/config.rs b/commons/zenoh-cfg-properties/src/config.rs deleted file mode 100644 index 68cfa03b8e..0000000000 --- a/commons/zenoh-cfg-properties/src/config.rs +++ /dev/null @@ -1,370 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -/// `"true"` -pub const ZN_TRUE: &str = "true"; -/// `"false"` -pub const ZN_FALSE: &str = "false"; -/// `"auto"` -pub const ZN_AUTO: &str = "auto"; - -/// The library mode. -/// String key: `"mode"`. -/// Accepted values: `"peer"`, `"client"`. -/// Default value: `"peer"`. -pub const ZN_MODE_KEY: u64 = 0x40; -pub const ZN_MODE_STR: &str = "mode"; -pub const ZN_MODE_DEFAULT: &str = "peer"; - -/// The locator of a peer to connect to. -/// String key: `"connect"`. -/// Accepted values: `` (ex: `"tcp/10.10.10.10:7447"`). -/// Default value: None. -/// Multiple values accepted. -pub const ZN_CONNECT_KEY: u64 = 0x41; -pub const ZN_CONNECT_STR: &str = "connect"; - -/// A locator to listen on. -/// String key: `"listen"`. -/// Accepted values: `` (ex: `"tcp/10.10.10.10:7447"`). -/// Default value: None. -/// Multiple values accepted. -pub const ZN_LISTEN_KEY: u64 = 0x42; -pub const ZN_LISTEN_STR: &str = "listen"; - -/// The user name to use for authentication. -/// String key: `"user"`. -/// Accepted values: ``. -pub const ZN_USER_KEY: u64 = 0x43; -pub const ZN_USER_STR: &str = "user"; - -/// The password to use for authentication. -/// String key: `"password"`. -/// Accepted values: ``. -pub const ZN_PASSWORD_KEY: u64 = 0x44; -pub const ZN_PASSWORD_STR: &str = "password"; - -/// Activates/Desactivates multicast scouting. -/// String key: `"multicast_scouting"`. -/// Accepted values: `"true"`, `"false"`. -/// Default value: `"true"`. -pub const ZN_MULTICAST_SCOUTING_KEY: u64 = 0x45; -pub const ZN_MULTICAST_SCOUTING_STR: &str = "multicast_scouting"; -pub const ZN_MULTICAST_SCOUTING_DEFAULT: &str = ZN_TRUE; - -/// The network interface to use for multicast scouting. -/// String key: `"multicast_interface"`. -/// Accepted values: `"auto"`, ``, ``. -/// Default value: `"auto"`. -pub const ZN_MULTICAST_INTERFACE_KEY: u64 = 0x46; -pub const ZN_MULTICAST_INTERFACE_STR: &str = "multicast_interface"; -pub const ZN_MULTICAST_INTERFACE_DEFAULT: &str = ZN_AUTO; - -/// The multicast IPv4 address and ports to use for multicast scouting. -/// String key: `"multicast_ipv4_address"`. -/// Accepted values: `:`. -/// Default value: `"224.0.0.224:7446"`. -pub const ZN_MULTICAST_IPV4_ADDRESS_KEY: u64 = 0x47; -pub const ZN_MULTICAST_IPV4_ADDRESS_STR: &str = "multicast_ipv4_address"; -pub const ZN_MULTICAST_IPV4_ADDRESS_DEFAULT: &str = "224.0.0.224:7446"; - -/// In client mode, the period dedicated to scouting a router before failing. -/// String key: `"scouting_timeout"`. -/// Accepted values: ``. -/// Default value: `"3.0"`. -pub const ZN_SCOUTING_TIMEOUT_KEY: u64 = 0x48; -pub const ZN_SCOUTING_TIMEOUT_STR: &str = "scouting_timeout"; -pub const ZN_SCOUTING_TIMEOUT_DEFAULT: &str = "3.0"; - -/// In peer mode, the period dedicated to scouting first remote peers before doing anything else. -/// String key: `"scouting_delay"`. -/// Accepted values: ``. -/// Default value: `"0.2"`. -pub const ZN_SCOUTING_DELAY_KEY: u64 = 0x49; -pub const ZN_SCOUTING_DELAY_STR: &str = "scouting_delay"; -pub const ZN_SCOUTING_DELAY_DEFAULT: &str = "0.2"; - -/// Indicates if data messages should be timestamped. -/// String key: `"add_timestamp"`. -/// Accepted values: `"true"`, `"false"`. -/// Default value: `"false"`. -pub const ZN_ADD_TIMESTAMP_KEY: u64 = 0x4A; -pub const ZN_ADD_TIMESTAMP_STR: &str = "add_timestamp"; -pub const ZN_ADD_TIMESTAMP_DEFAULT: &str = ZN_FALSE; - -/// Indicates if the link state protocol should run. -/// String key: `"link_state"`. -/// Accepted values: `"true"`, `"false"`. -/// Default value: `"true"`. -pub const ZN_LINK_STATE_KEY: u64 = 0x4B; -pub const ZN_LINK_STATE_STR: &str = "link_state"; -pub const ZN_LINK_STATE_DEFAULT: &str = ZN_TRUE; - -/// The file path containing the user password dictionary. -/// String key: `"user_password_dictionary"`. -/// Accepted values: ``. -pub const ZN_USER_PASSWORD_DICTIONARY_KEY: u64 = 0x4C; -pub const ZN_USER_PASSWORD_DICTIONARY_STR: &str = "user_password_dictionary"; - -/// Indicates if peers should connect to each other -/// when they discover each other (through multicast -/// or gossip discovery). -/// String key: `"peers_autoconnect"`. -/// Accepted values: `"true"`, `"false"`. -/// Default value: `"true"`. -pub const ZN_PEERS_AUTOCONNECT_KEY: u64 = 0x4D; -pub const ZN_PEERS_AUTOCONNECT_STR: &str = "peers_autoconnect"; -pub const ZN_PEERS_AUTOCONNECT_DEFAULT: &str = ZN_TRUE; - -/// The file path containing the TLS server private key. -/// String key: `"tls_server_private_key"`. -/// Accepted values: ``. -/// Default value: None. -pub const ZN_TLS_SERVER_PRIVATE_KEY_KEY: u64 = 0x4E; -pub const ZN_TLS_SERVER_PRIVATE_KEY_STR: &str = "tls_server_private_key"; - -/// The file path containing the TLS server certificate. -/// String key: `"tls_server_certificate"`. -/// Accepted values: ``. -/// Default value: None. -pub const ZN_TLS_SERVER_CERTIFICATE_KEY: u64 = 0x4F; -pub const ZN_TLS_SERVER_CERTIFICATE_STR: &str = "tls_server_certificate"; - -/// The file path containing the TLS root CA certificate. -/// String key: `"tls_root_ca_certificate"`. -/// Accepted values: ``. -/// Default value: None. -pub const ZN_TLS_ROOT_CA_CERTIFICATE_KEY: u64 = 0x50; -pub const ZN_TLS_ROOT_CA_CERTIFICATE_STR: &str = "tls_root_ca_certificate"; - -/// Indicates if the shared-memory features should be used. -/// String key: `"shm"`. -/// Accepted values: `"true"`, `"false"`. -/// Default value: `"true"`. -pub const ZN_SHM_KEY: u64 = 0x51; -pub const ZN_SHM_STR: &str = "shm"; -pub const ZN_SHM_DEFAULT: &str = ZN_TRUE; - -/// Indicates if routers should connect to each other -/// when they discover each other through multicast. -/// String key: `"routers_autoconnect_multicast"`. -/// Accepted values: `"true"`, `"false"`. -/// Default value: `"false"`. -pub const ZN_ROUTERS_AUTOCONNECT_MULTICAST_KEY: u64 = 0x52; -pub const ZN_ROUTERS_AUTOCONNECT_MULTICAST_STR: &str = "routers_autoconnect_multicast"; -pub const ZN_ROUTERS_AUTOCONNECT_MULTICAST_DEFAULT: &str = ZN_FALSE; - -/// Indicates if routers should connect to each other -/// when they discover each other through gossip discovery. -/// String key: `"routers_autoconnect_gossip"`. -/// Accepted values: `"true"`, `"false"`. -/// Default value: `"false"`. -pub const ZN_ROUTERS_AUTOCONNECT_GOSSIP_KEY: u64 = 0x53; -pub const ZN_ROUTERS_AUTOCONNECT_GOSSIP_STR: &str = "routers_autoconnect_gossip"; -pub const ZN_ROUTERS_AUTOCONNECT_GOSSIP_DEFAULT: &str = ZN_FALSE; - -pub const ZN_JOIN_SUBSCRIPTIONS_KEY: u64 = 0x61; -pub const ZN_JOIN_SUBSCRIPTIONS_STR: &str = "join_subscriptions"; - -pub const ZN_JOIN_PUBLICATIONS_KEY: u64 = 0x62; -pub const ZN_JOIN_PUBLICATIONS_STR: &str = "join_publications"; - -/// Configures the link lease expressed in milliseconds. -/// String key: `"link_lease"`. -/// Accepted values: ``. -/// Default value: `10000 (10 seconds)`. -pub const ZN_LINK_LEASE_KEY: u64 = 0x63; -pub const ZN_LINK_LEASE_STR: &str = "link_lease"; -pub const ZN_LINK_LEASE_DEFAULT: &str = "10000"; - -/// Configures the number of keep-alive messages in a link lease duration. -/// String key: `"link_keep_alive"`. -/// Accepted values: ``. -/// Default value: `4 (2.5 seconds)`. -pub const ZN_LINK_KEEP_ALIVE_KEY: u64 = 0x64; -pub const ZN_LINK_KEEP_ALIVE_STR: &str = "link_keep_alive"; -pub const ZN_LINK_KEEP_ALIVE_DEFAULT: &str = "4"; - -/// Configures the sequence number resolution. -/// String key: `"seq_num_resolution"`. -/// Accepted values: ``. -/// Default value: `268435456` (2^28). -pub const ZN_SEQ_NUM_RESOLUTION_KEY: u64 = 0x65; -pub const ZN_SEQ_NUM_RESOLUTION_STR: &str = "seq_num_resolution"; -pub const ZN_SEQ_NUM_RESOLUTION_DEFAULT: &str = "268435456"; - -/// Configures the timeout in milliseconds when opening a link. -/// String key: `"open_timeout"`. -/// Accepted values: ``. -/// Default value: `10000`. -pub const ZN_OPEN_TIMEOUT_KEY: u64 = 0x66; -pub const ZN_OPEN_TIMEOUT_STR: &str = "open_timeout"; -pub const ZN_OPEN_TIMEOUT_DEFAULT: &str = "10000"; - -/// Configures the number of open session that can be in pending state. -/// String key: `"open_pending"`. -/// Accepted values: ``. -/// Default value: `1024`. -pub const ZN_OPEN_INCOMING_PENDING_KEY: u64 = 0x67; -pub const ZN_OPEN_INCOMING_PENDING_STR: &str = "open_pending"; -pub const ZN_OPEN_INCOMING_PENDING_DEFAULT: &str = "100"; - -/// Configures the peer ID. -/// String key: `"peer_id"`. -/// Accepted values: ``. -pub const ZN_PEER_ID_KEY: u64 = 0x68; -pub const ZN_PEER_ID_STR: &str = "peer_id"; - -/// Configures the batch size. -/// String key: `"batch_size"`. -/// Accepted values: ``. -/// Default value: `65535`. -pub const ZN_BATCH_SIZE_KEY: u64 = 0x69; -pub const ZN_BATCH_SIZE_STR: &str = "batch_size"; -pub const ZN_BATCH_SIZE_DEFAULT: &str = "65535"; - -/// Configures the maximum number of simultaneous open unicast sessions. -/// String key: `"max_sessions_unicast"`. -/// Accepted values: ``. -/// Default value: `1024`. -pub const ZN_MAX_SESSIONS_UNICAST_KEY: u64 = 0x70; -pub const ZN_MAX_SESSIONS_UNICAST_STR: &str = "max_sessions_unicast"; -pub const ZN_MAX_SESSIONS_UNICAST_DEFAULT: &str = "1024"; - -/// Configures the maximum number of inbound links per open session. -/// String key: `"max_links"`. -/// Accepted values: ``. -/// Default value: `1`. -pub const ZN_MAX_LINKS_KEY: u64 = 0x71; -pub const ZN_MAX_LINKS_STR: &str = "max_links"; -pub const ZN_MAX_LINKS_DEFAULT: &str = "1"; - -/// Configures the zenoh version. -/// String key: `"version"`. -/// Accepted values: ``. -pub const ZN_VERSION_KEY: u64 = 0x72; -pub const ZN_VERSION_STR: &str = "version"; - -/// Configures the QoS support. -/// String key: `"qos"`. -/// Accepted values: `"true"`, `"false"`. -/// Default value: `"true"`. -pub const ZN_QOS_KEY: u64 = 0x73; -pub const ZN_QOS_STR: &str = "qos"; -pub const ZN_QOS_DEFAULT: &str = ZN_TRUE; - -/// Configures the link keep alive expressed in milliseconds. -/// String key: `"join_interval"`. -/// Accepted values: ``. -/// Default value: `2500`. -pub const ZN_JOIN_INTERVAL_KEY: u64 = 0x74; -pub const ZN_JOIN_INTERVAL_STR: &str = "join_interval"; -pub const ZN_JOIN_INTERVAL_DEFAULT: &str = "2500"; - -/// Configures the maximum size in bytes of the defragmentation -/// buffer at receiving side. Messages that have been fragmented -/// and that are larger than the configured size will be dropped. -/// String key: `"defrag_buff_size"`. -/// Accepted values: ``. -/// Default value: `1073741824` (1GiB). -pub const ZN_DEFRAG_BUFF_SIZE_KEY: u64 = 0x75; -pub const ZN_DEFRAG_BUFF_SIZE_STR: &str = "defrag_buff_size"; -pub const ZN_DEFRAG_BUFF_SIZE_DEFAULT: &str = "1073741824"; - -/// Configures the buffer size in bytes at receiving side for each link. -/// String key: `"link_rx_buff_size"`. -/// Accepted values: ``. -/// Default value: `65535` (64KiB). -pub const ZN_LINK_RX_BUFF_SIZE_KEY: u64 = 0x76; -pub const ZN_LINK_RX_BUFF_SIZE_STR: &str = "link_rx_buff_size"; -pub const ZN_LINK_RX_BUFF_SIZE_DEFAULT: &str = "65535"; - -/// The multicast IPv6 address and ports to use for multicast scouting. -/// String key: `"multicast_ipv6_address"`. -/// Accepted values: `:`. -/// Default value: `"[ff24::224]:7446"`. -pub const ZN_MULTICAST_IPV6_ADDRESS_KEY: u64 = 0x77; -pub const ZN_MULTICAST_IPV6_ADDRESS_STR: &str = "multicast_ipv6_address"; -pub const ZN_MULTICAST_IPV6_ADDRESS_DEFAULT: &str = "[ff24::224]:7446"; - -/// The public RSA key. -/// String key: `"auth_rsa_public_key_pem"`. -/// Accepted values: ``. -pub const ZN_AUTH_RSA_PUBLIC_KEY_PEM_KEY: u64 = 0x78; -pub const ZN_AUTH_RSA_PUBLIC_KEY_PEM_STR: &str = "auth_rsa_public_key_pem"; - -/// The private RSA key. -/// String key: `"auth_rsa_private_key_pem"`. -/// Accepted values: ``. -pub const ZN_AUTH_RSA_PRIVATE_KEY_PEM_KEY: u64 = 0x79; -pub const ZN_AUTH_RSA_PRIVATE_KEY_PEM_STR: &str = "auth_rsa_private_key_pem"; - -/// The public RSA key. -/// String key: `"auth_rsa_public_key_pem"`. -/// Accepted values: ``. -pub const ZN_AUTH_RSA_PUBLIC_KEY_FILE_KEY: u64 = 0x80; -pub const ZN_AUTH_RSA_PUBLIC_KEY_FILE_STR: &str = "auth_rsa_public_key_file"; - -/// The private RSA key. -/// String key: `"auth_rsa_private_key_pem"`. -/// Accepted values: ``. -pub const ZN_AUTH_RSA_PRIVATE_KEY_FILE_KEY: u64 = 0x81; -pub const ZN_AUTH_RSA_PRIVATE_KEY_FILE_STR: &str = "auth_rsa_private_key_file"; - -/// The default RSA key size. -/// String key: `"auth_rsa_key_size"`. -/// Accepted values: ``. -pub const ZN_AUTH_RSA_KEY_SIZE_KEY: u64 = 0x82; -pub const ZN_AUTH_RSA_KEY_SIZE_STR: &str = "auth_rsa_key_size"; -pub const ZN_AUTH_RSA_KEY_SIZE_DEFAULT: &str = "512"; - -/// The list of known RSA public keys. -/// String key: `"auth_rsa_known_keys_file"`. -/// Accepted values: ``. -pub const ZN_AUTH_RSA_KNOWN_KEYS_FILE_KEY: u64 = 0x83; -pub const ZN_AUTH_RSA_KNOWN_KEYS_FILE_STR: &str = "auth_rsa_known_keys_file"; - -/// Configures the maximum number of simultaneous open unicast sessions. -/// String key: `"max_sessions_unicast"`. -/// Accepted values: ``. -/// Default value: `1024`. -pub const ZN_MAX_SESSIONS_MULTICAST_KEY: u64 = 0x84; -pub const ZN_MAX_SESSIONS_MULTICAST_STR: &str = "max_sessions_multicast"; -pub const ZN_MAX_SESSIONS_MULTICAST_DEFAULT: &str = "1024"; - -/// The file path containing the TLS client private key. -pub const ZN_TLS_CLIENT_PRIVATE_KEY_KEY: u64 = 0x85; -pub const ZN_TLS_CLIENT_PRIVATE_KEY_STR: &str = "tls_client_private_key"; - -/// The file path containing the TLS client certificate. -pub const ZN_TLS_CLIENT_CERTIFICATE_KEY: u64 = 0x86; -pub const ZN_TLS_CLIENT_CERTIFICATE_STR: &str = "tls_client_certificate"; - -/// Whether to use tls with client authentication -pub const ZN_TLS_CLIENT_AUTH_KEY: u64 = 0x87; -pub const ZN_TLS_CLIENT_AUTH_STR: &str = "tls_client_auth"; -pub const ZN_TLS_CLIENT_AUTH_DEFAULT: &str = ZN_FALSE; - -/// The default timeout to apply to queries in milliseconds. -/// String key: `"queries_default_timeout"`. -/// Accepted values: ``. -/// Default value: `10000`. -pub const ZN_QUERIES_DEFAULT_TIMEOUT_KEY: u64 = 0x88; -pub const ZN_QUERIES_DEFAULT_TIMEOUT_STR: &str = "local_routing"; -pub const ZN_QUERIES_DEFAULT_TIMEOUT_DEFAULT: &str = "10000"; - -/// Whether or not to verify that servers have certs valid for their ip or common name -pub const ZN_TLS_SERVER_NAME_VERIFICATION_KEY: u64 = 0x89; -pub const ZN_TLS_SERVER_NAME_VERIFICATION_STR: &str = "server_name_verification"; -pub const ZN_TLS_SERVER_NAME_VERIFICATION_DEFAULT: &str = ZN_TRUE; diff --git a/commons/zenoh-cfg-properties/src/lib.rs b/commons/zenoh-cfg-properties/src/lib.rs deleted file mode 100644 index 074c72aee3..0000000000 --- a/commons/zenoh-cfg-properties/src/lib.rs +++ /dev/null @@ -1,358 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -//! ⚠️ WARNING ⚠️ -//! -//! This crate is depecrated and it will be completely removed in the future. -//! -//! This crate is intended for Zenoh's internal use. -//! -//! [Click here for Zenoh's documentation](../zenoh/index.html) -pub mod config; - -use std::collections::HashMap; -use std::convert::{From, TryFrom}; -use std::fmt; -use std::marker::PhantomData; -use std::ops::{Deref, DerefMut}; - -pub trait KeyTranscoder { - fn encode(key: &str) -> Option; - fn decode(key: u64) -> Option; -} - -/// A set of Key/Value (`u64`/`String`) pairs. -#[non_exhaustive] -#[derive(PartialEq, Eq)] -pub struct IntKeyProperties(pub HashMap, PhantomData) -where - T: KeyTranscoder; - -impl IntKeyProperties { - #[inline] - pub fn get_or<'a>(&'a self, key: &u64, default: &'a str) -> &'a str { - self.get(key).map(|s| &s[..]).unwrap_or(default) - } -} - -impl Clone for IntKeyProperties { - fn clone(&self) -> Self { - Self(self.0.clone(), PhantomData) - } -} - -impl Default for IntKeyProperties { - fn default() -> Self { - Self(HashMap::new(), PhantomData) - } -} - -impl Deref for IntKeyProperties { - type Target = HashMap; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for IntKeyProperties { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl fmt::Display for IntKeyProperties { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&Properties::from(self.clone()), f) - } -} - -impl fmt::Debug for IntKeyProperties { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(&Properties::from(self.clone()), f) - } -} - -impl From> for HashMap { - fn from(props: IntKeyProperties) -> Self { - props - .0 - .into_iter() - .filter_map(|(k, v)| T::decode(k).map(|k| (k, v))) - .collect() - } -} - -impl From> for IntKeyProperties { - fn from(map: HashMap) -> Self { - Self(map, PhantomData) - } -} - -impl From> for IntKeyProperties { - fn from(map: HashMap) -> Self { - Self( - map.into_iter() - .filter_map(|(k, v)| T::encode(&k).map(|k| (k, v))) - .collect(), - PhantomData, - ) - } -} - -impl From for IntKeyProperties { - fn from(props: Properties) -> Self { - props.0.into() - } -} - -impl From<&str> for IntKeyProperties { - fn from(s: &str) -> Self { - Properties::from(s).into() - } -} - -impl From for IntKeyProperties { - fn from(s: String) -> Self { - Properties::from(s).into() - } -} - -impl From<&[(&str, &str)]> for IntKeyProperties { - fn from(kvs: &[(&str, &str)]) -> Self { - Self( - kvs.iter() - .filter_map(|(k, v)| T::encode(k).map(|k| (k, v.to_string()))) - .collect(), - PhantomData, - ) - } -} - -impl From<&[(u64, &str)]> for IntKeyProperties { - fn from(kvs: &[(u64, &str)]) -> Self { - Self( - kvs.iter() - .cloned() - .map(|(k, v)| (k, v.to_string())) - .collect(), - PhantomData, - ) - } -} - -static PROP_SEPS: &[&str] = &["\r\n", "\n", ";"]; -const DEFAULT_PROP_SEP: char = ';'; -const KV_SEP: &[char] = &['=', ':']; -const COMMENT_PREFIX: char = '#'; - -/// A map of key/value (String,String) properties. -/// -/// It can be parsed from a String, using `;` or `` as separator between each properties -/// and `=` as separator between a key and its value. Keys and values are trimed. -#[non_exhaustive] -#[derive(Clone, PartialEq, Eq, Default)] -pub struct Properties(pub HashMap); - -impl Deref for Properties { - type Target = HashMap; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for Properties { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl fmt::Display for Properties { - /// Format the Properties as a string, using `'='` for key/value separator - /// and `';'` for separator between each keys/values. - /// - /// **WARNING**: the passwords are displayed in clear. This is required for the result - /// of the [`trait@ToString`] automatic implementation that must preserve all the properties. - /// To display the properties, hidding the passwords, rather use the [`Debug`](core::fmt::Debug) trait implementation. - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut it = self.0.iter(); - if let Some((k, v)) = it.next() { - if v.is_empty() { - write!(f, "{k}")? - } else { - write!(f, "{}{}{}", k, KV_SEP[0], v)? - } - for (k, v) in it { - if v.is_empty() { - write!(f, "{DEFAULT_PROP_SEP}{k}")? - } else { - write!(f, "{}{}{}{}", DEFAULT_PROP_SEP, k, KV_SEP[0], v)? - } - } - } - Ok(()) - } -} - -impl fmt::Debug for Properties { - /// Format the Properties as a string, using `'='` for key/value separator - /// and `';'` for separator between each keys/values. - /// - /// **NOTE**: for each key containing `"password"` as sub-string, - /// the value is replaced by `"*****"`. - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut it = self.0.iter(); - if let Some((k, v)) = it.next() { - if v.is_empty() { - write!(f, "{k}")? - } else if k.contains("password") { - write!(f, "{}{}*****", k, KV_SEP[0])? - } else { - write!(f, "{}{}{}", k, KV_SEP[0], v)? - } - for (k, v) in it { - if v.is_empty() { - write!(f, "{DEFAULT_PROP_SEP}{k}")? - } else if k.contains("password") { - write!(f, "{}{}{}*****", DEFAULT_PROP_SEP, k, KV_SEP[0])? - } else { - write!(f, "{}{}{}{}", DEFAULT_PROP_SEP, k, KV_SEP[0], v)? - } - } - } - Ok(()) - } -} - -impl From<&str> for Properties { - fn from(s: &str) -> Self { - let mut props = vec![s]; - for sep in PROP_SEPS { - props = props - .into_iter() - .flat_map(|s| s.split(sep)) - .collect::>(); - } - props = props.into_iter().map(|s| s.trim()).collect::>(); - - Properties( - props - .iter() - .filter_map(|prop| { - if prop.is_empty() || prop.starts_with(COMMENT_PREFIX) { - None - } else { - let mut it = prop.splitn(2, KV_SEP); - Some(( - it.next().unwrap().trim().to_string(), - it.next().unwrap_or("").trim().to_string(), - )) - } - }) - .collect(), - ) - } -} - -impl From for Properties { - fn from(s: String) -> Self { - Self::from(s.as_str()) - } -} - -impl From> for Properties { - fn from(map: HashMap) -> Self { - Self(map) - } -} - -impl From> for Properties { - fn from(props: IntKeyProperties) -> Self { - Self( - props - .0 - .into_iter() - .filter_map(|(k, v)| T::decode(k).map(|k| (k, v))) - .collect(), - ) - } -} - -impl From<&[(&str, &str)]> for Properties { - fn from(kvs: &[(&str, &str)]) -> Self { - Properties( - kvs.iter() - .map(|(k, v)| (k.to_string(), v.to_string())) - .collect(), - ) - } -} - -impl TryFrom<&std::path::Path> for Properties { - type Error = zenoh_result::Error; - fn try_from(p: &std::path::Path) -> Result { - Ok(Self::from(std::fs::read_to_string(p)?)) - } -} - -#[non_exhaustive] -pub struct DummyTranscoder; - -impl KeyTranscoder for DummyTranscoder { - fn encode(_key: &str) -> Option { - None - } - - fn decode(_key: u64) -> Option { - None - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_properties() { - assert!(Properties::from("").0.is_empty()); - - assert_eq!(Properties::from("p1"), Properties::from(&[("p1", "")][..])); - - assert_eq!( - Properties::from("p1=v1"), - Properties::from(&[("p1", "v1")][..]) - ); - - assert_eq!( - Properties::from("p1=v1;p2=v2;"), - Properties::from(&[("p1", "v1"), ("p2", "v2")][..]) - ); - - assert_eq!( - Properties::from("p1=v1;p2;p3=v3"), - Properties::from(&[("p1", "v1"), ("p2", ""), ("p3", "v3")][..]) - ); - - assert_eq!( - Properties::from("p1=v 1;p 2=v2"), - Properties::from(&[("p1", "v 1"), ("p 2", "v2")][..]) - ); - - assert_eq!( - Properties::from("p1=x=y;p2=a==b"), - Properties::from(&[("p1", "x=y"), ("p2", "a==b")][..]) - ); - } -} diff --git a/commons/zenoh-codec/Cargo.toml b/commons/zenoh-codec/Cargo.toml index 5510823f89..3e103a7b1a 100644 --- a/commons/zenoh-codec/Cargo.toml +++ b/commons/zenoh-codec/Cargo.toml @@ -31,6 +31,8 @@ description = "Internal crate for zenoh." [features] default = ["std"] std = [ + "log", + "serde/std", "uhlc/std", "zenoh-protocol/std" ] @@ -42,6 +44,8 @@ shared-memory = [ complete_n = ["zenoh-protocol/complete_n"] [dependencies] +log = { workspace = true, optional = true } +serde = { workspace = true, features = ["alloc"] } uhlc = { workspace = true } zenoh-buffers = { workspace = true, default-features = false } zenoh-protocol = { workspace = true } @@ -50,6 +54,7 @@ zenoh-shm = { workspace = true, optional = true } # INFO: May cause problems when testing no_std stuff. Check this tool: https://docs.rs/crate/cargo-no-dev-deps/0.1.0 [dev-dependencies] criterion = { workspace = true } +env_logger = { workspace = true } rand = { workspace = true, features = ["default"] } uuid = { workspace = true, features = ["default"] } zenoh-protocol = { workspace = true, features = ["test"] } diff --git a/commons/zenoh-codec/benches/codec.rs b/commons/zenoh-codec/benches/codec.rs index 10a27b130f..2c786a41db 100644 --- a/commons/zenoh-codec/benches/codec.rs +++ b/commons/zenoh-codec/benches/codec.rs @@ -14,6 +14,8 @@ #[macro_use] extern crate criterion; +use std::sync::Arc; + use criterion::Criterion; use zenoh_buffers::{ reader::{DidntRead, HasReader}, @@ -22,68 +24,76 @@ use zenoh_buffers::{ }; use zenoh_codec::*; use zenoh_protocol::{ - core::{Channel, CongestionControl, ZInt}, - defaults::BATCH_SIZE, - transport::{Frame, FrameHeader, FrameKind}, - zenoh::Data, + core::{Encoding, Reliability, WireExpr}, + network::{ext, Push}, + transport::{BatchSize, Frame, FrameHeader, TransportSn}, + zenoh::{PushBody, Put}, }; fn criterion_benchmark(c: &mut Criterion) { - // ZInt Vec + // u64 Vec let mut buff = vec![]; - let codec = Zenoh060::default(); - c.bench_function("ZInt Vec", |b| { + let codec = Zenoh080::new(); + c.bench_function("u64 Vec", |b| { b.iter(|| { buff.clear(); let mut writer = buff.writer(); - codec.write(&mut writer, ZInt::MAX).unwrap(); + codec.write(&mut writer, u64::MAX).unwrap(); let mut reader = buff.reader(); - let _: ZInt = codec.read(&mut reader).unwrap(); + let _: u64 = codec.read(&mut reader).unwrap(); }) }); - // ZInt BBuf - let mut buff = BBuf::with_capacity(BATCH_SIZE as usize); - let codec = Zenoh060::default(); - c.bench_function("ZInt BBuf", |b| { + // u64 BBuf + let mut buff = BBuf::with_capacity(BatchSize::MAX as usize); + let codec = Zenoh080::new(); + c.bench_function("u64 BBuf", |b| { b.iter(|| { buff.clear(); let mut writer = buff.writer(); - codec.write(&mut writer, ZInt::MAX).unwrap(); + codec.write(&mut writer, u64::MAX).unwrap(); let mut reader = buff.reader(); - let _: ZInt = codec.read(&mut reader).unwrap(); + let _: u64 = codec.read(&mut reader).unwrap(); }) }); - // ZInt ZBuf - let mut buff = ZBuf::default(); - let codec = Zenoh060::default(); - c.bench_function("ZInt ZBuf", |b| { + // u64 ZBuf + let mut buff = ZBuf::empty(); + let codec = Zenoh080::new(); + c.bench_function("u64 ZBuf", |b| { b.iter(|| { buff.clear(); let mut writer = buff.writer(); - codec.write(&mut writer, ZInt::MAX).unwrap(); + codec.write(&mut writer, u64::MAX).unwrap(); let mut reader = buff.reader(); - let _: ZInt = codec.read(&mut reader).unwrap(); + let _: u64 = codec.read(&mut reader).unwrap(); }) }); // Batch BBuf Write let mut buff = BBuf::with_capacity(u16::MAX as usize); - let codec = Zenoh060::default(); + let codec = Zenoh080::new(); let frame = FrameHeader { - channel: Channel::default(), - sn: ZInt::MIN, - kind: FrameKind::Messages, + reliability: Reliability::default(), + sn: TransportSn::MIN, + ext_qos: zenoh_protocol::transport::frame::ext::QoSType::default(), }; - let data = Data { - key: 0.into(), - data_info: None, - payload: ZBuf::from(vec![0u8; 8]), - congestion_control: CongestionControl::default(), - reply_context: None, + let data = Push { + wire_expr: WireExpr::empty(), + ext_qos: ext::QoSType::default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + payload: PushBody::Put(Put { + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + payload: ZBuf::from(vec![0u8; 8]), + }), }; // Calculate the number of messages @@ -107,20 +117,28 @@ fn criterion_benchmark(c: &mut Criterion) { // Batch ZSlice Read NoAlloc let mut buff = BBuf::with_capacity(u16::MAX as usize); - let codec = Zenoh060::default(); + let codec = Zenoh080::new(); let frame = FrameHeader { - channel: Channel::default(), - sn: ZInt::MIN, - kind: FrameKind::Messages, + reliability: Reliability::default(), + sn: TransportSn::MIN, + ext_qos: zenoh_protocol::transport::frame::ext::QoSType::default(), }; - let data = Data { - key: 0.into(), - data_info: None, - payload: ZBuf::from(vec![0u8; 8]), - congestion_control: CongestionControl::default(), - reply_context: None, + let data = Push { + wire_expr: WireExpr::empty(), + ext_qos: ext::QoSType::default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + payload: PushBody::Put(Put { + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + payload: ZBuf::from(vec![0u8; 8]), + }), }; let mut writer = buff.writer(); @@ -139,20 +157,28 @@ fn criterion_benchmark(c: &mut Criterion) { // Batch ZSlice Read NoAlloc let mut buff = BBuf::with_capacity(u16::MAX as usize); - let codec = Zenoh060::default(); + let codec = Zenoh080::new(); let frame = FrameHeader { - channel: Channel::default(), - sn: ZInt::MIN, - kind: FrameKind::Messages, + reliability: Reliability::default(), + sn: TransportSn::MIN, + ext_qos: zenoh_protocol::transport::frame::ext::QoSType::default(), }; - let data = Data { - key: 0.into(), - data_info: None, - payload: ZBuf::from(vec![0u8; 8]), - congestion_control: CongestionControl::default(), - reply_context: None, + let data = Push { + wire_expr: WireExpr::empty(), + ext_qos: ext::QoSType::default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + payload: PushBody::Put(Put { + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + payload: ZBuf::from(vec![0u8; 8]), + }), }; let mut writer = buff.writer(); @@ -167,7 +193,7 @@ fn criterion_benchmark(c: &mut Criterion) { let _header: FrameHeader = codec.read(&mut reader).unwrap(); loop { - let res: Result = codec.read(&mut reader); + let res: Result = codec.read(&mut reader); if res.is_err() { break; } @@ -176,16 +202,25 @@ fn criterion_benchmark(c: &mut Criterion) { }); // Fragmentation ZBuf Write - let mut buff = ZBuf::default(); - let codec = Zenoh060::default(); - - let data = Data { - key: 0.into(), - data_info: None, - payload: ZBuf::from(vec![0u8; 1_000_000]), - congestion_control: CongestionControl::default(), - reply_context: None, + let mut buff = ZBuf::empty(); + let codec = Zenoh080::new(); + + let data = Push { + wire_expr: WireExpr::empty(), + ext_qos: ext::QoSType::default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + payload: PushBody::Put(Put { + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + payload: ZBuf::from(vec![0u8; 1_000_000]), + }), }; + c.bench_function("Fragmentation ZBuf Write", |b| { b.iter(|| { let mut writer = buff.writer(); @@ -195,20 +230,28 @@ fn criterion_benchmark(c: &mut Criterion) { // Fragmentation ZBuf Read let mut buff = vec![]; - let codec = Zenoh060::default(); - - let data = Data { - key: 0.into(), - data_info: None, - payload: ZBuf::from(vec![0u8; 1_000_000]), - congestion_control: CongestionControl::default(), - reply_context: None, + let codec = Zenoh080::new(); + + let data = Push { + wire_expr: WireExpr::empty(), + ext_qos: ext::QoSType::default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + payload: PushBody::Put(Put { + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + payload: ZBuf::from(vec![0u8; 1_000_000]), + }), }; let mut writer = buff.writer(); codec.write(&mut writer, &data).unwrap(); - let mut zbuf = ZBuf::default(); + let mut zbuf = ZBuf::empty(); let chunk = u16::MAX as usize; let mut idx = 0; while idx < buff.len() { @@ -219,40 +262,49 @@ fn criterion_benchmark(c: &mut Criterion) { c.bench_function("Fragmentation ZBuf Read", |b| { b.iter(|| { let mut reader = zbuf.reader(); - let _data: Data = codec.read(&mut reader).unwrap(); + let _data: Push = codec.read(&mut reader).unwrap(); }) }); // Fragmentation ZSlice ZBuf Read let mut buff = vec![]; - let codec = Zenoh060::default(); - - let data = Data { - key: 0.into(), - data_info: None, - payload: ZBuf::from(vec![0u8; 1_000_000]), - congestion_control: CongestionControl::default(), - reply_context: None, + let codec = Zenoh080::new(); + + let data = Push { + wire_expr: WireExpr::empty(), + ext_qos: ext::QoSType::default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + payload: PushBody::Put(Put { + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + payload: ZBuf::from(vec![0u8; 1_000_000]), + }), }; let mut writer = buff.writer(); codec.write(&mut writer, &data).unwrap(); - let zslice: ZSlice = buff.into(); + let buff = Arc::new(buff); + let zslice: ZSlice = buff.clone().into(); c.bench_function("Fragmentation ZSlice ZBuf Read", |b| { b.iter(|| { - let mut zbuf = ZBuf::default(); + let mut zbuf = ZBuf::empty(); let chunk = u16::MAX as usize; let mut idx = 0; while idx < zslice.len() { let len = (zslice.len() - idx).min(chunk); - zbuf.push_zslice(ZSlice::make(zslice.buf.clone(), idx, idx + len).unwrap()); + zbuf.push_zslice(ZSlice::make(buff.clone(), idx, idx + len).unwrap()); idx += len; } let mut reader = zbuf.reader(); - let _data: Data = codec.read(&mut reader).unwrap(); + let _data: Push = codec.read(&mut reader).unwrap(); }) }); } diff --git a/commons/zenoh-codec/src/common/attachment.rs b/commons/zenoh-codec/src/common/attachment.rs deleted file mode 100644 index 76081ab586..0000000000 --- a/commons/zenoh-codec/src/common/attachment.rs +++ /dev/null @@ -1,104 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use crate::{RCodec, WCodec, Zenoh060, Zenoh060Header}; -use zenoh_buffers::{ - reader::{DidntRead, Reader}, - writer::{DidntWrite, Writer}, - ZBuf, -}; -use zenoh_protocol::{ - common::{imsg, Attachment}, - transport::tmsg, -}; -#[cfg(feature = "shared-memory")] -use {crate::Zenoh060Condition, core::any::TypeId, zenoh_shm::SharedMemoryBufInfoSerialized}; - -impl WCodec<&Attachment, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &Attachment) -> Self::Output { - // Header - #[allow(unused_mut)] // mut required with #[cfg(feature = "shared-memory")] - let mut header = tmsg::id::ATTACHMENT; - - #[cfg(feature = "shared-memory")] - { - let has_shminfo = x - .buffer - .zslices() - .any(|s| s.buf.as_any().type_id() == TypeId::of::()); - if has_shminfo { - header |= tmsg::flag::Z; - } - } - - self.write(&mut *writer, header)?; - - // Body - #[cfg(feature = "shared-memory")] - { - let codec = Zenoh060Condition::new(imsg::has_flag(header, tmsg::flag::Z)); - codec.write(&mut *writer, &x.buffer) - } - #[cfg(not(feature = "shared-memory"))] - { - self.write(&mut *writer, &x.buffer) - } - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; - codec.read(reader) - } -} - -impl RCodec for Zenoh060Header -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - if imsg::mid(self.header) != imsg::id::ATTACHMENT { - return Err(DidntRead); - } - - let buffer: ZBuf = { - #[cfg(feature = "shared-memory")] - { - let codec = Zenoh060Condition::new(imsg::has_flag(self.header, tmsg::flag::Z)); - codec.read(&mut *reader)? - } - #[cfg(not(feature = "shared-memory"))] - { - self.codec.read(&mut *reader)? - } - }; - - Ok(Attachment { buffer }) - } -} diff --git a/commons/zenoh-codec/src/common/extension.rs b/commons/zenoh-codec/src/common/extension.rs new file mode 100644 index 0000000000..4215711815 --- /dev/null +++ b/commons/zenoh-codec/src/common/extension.rs @@ -0,0 +1,428 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::{RCodec, WCodec, Zenoh080, Zenoh080Bounded, Zenoh080Header}; +use alloc::vec::Vec; +use zenoh_buffers::{ + reader::{DidntRead, Reader}, + writer::{DidntWrite, Writer}, + ZBuf, +}; +use zenoh_protocol::common::{ + iext, imsg::has_flag, ZExtBody, ZExtUnit, ZExtUnknown, ZExtZ64, ZExtZBuf, ZExtZBufHeader, +}; + +fn read_inner(reader: &mut R, _s: &str, header: u8) -> Result<(ZExtUnknown, bool), DidntRead> +where + R: Reader, +{ + let codec = Zenoh080Header::new(header); + let (u, has_ext): (ZExtUnknown, bool) = codec.read(&mut *reader)?; + if u.is_mandatory() { + #[cfg(feature = "std")] + log::error!("Unknown {_s} ext: {u:?}"); + return Err(DidntRead); + } else { + #[cfg(feature = "std")] + log::debug!("Unknown {_s} ext: {u:?}"); + } + Ok((u, has_ext)) +} + +#[cold] +#[inline(never)] +pub fn read(reader: &mut R, s: &str, header: u8) -> Result<(ZExtUnknown, bool), DidntRead> +where + R: Reader, +{ + read_inner(&mut *reader, s, header) +} + +fn skip_inner(reader: &mut R, s: &str, header: u8) -> Result +where + R: Reader, +{ + let (_, has_ext): (ZExtUnknown, bool) = read_inner(&mut *reader, s, header)?; + Ok(has_ext) +} + +#[cold] +#[inline(never)] +pub fn skip(reader: &mut R, s: &str, header: u8) -> Result +where + R: Reader, +{ + skip_inner(reader, s, header) +} + +#[cold] +#[inline(never)] +pub fn skip_all(reader: &mut R, s: &str) -> Result<(), DidntRead> +where + R: Reader, +{ + let codec = Zenoh080::new(); + let mut has_ext = true; + while has_ext { + let header: u8 = codec.read(&mut *reader)?; + has_ext = skip_inner(reader, s, header)?; + } + Ok(()) +} + +// ZExtUnit +impl WCodec<(&ZExtUnit<{ ID }>, bool), &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: (&ZExtUnit<{ ID }>, bool)) -> Self::Output { + let (_x, more) = x; + let mut header: u8 = ID; + if more { + header |= iext::FLAG_Z; + } + self.write(&mut *writer, header)?; + Ok(()) + } +} + +impl RCodec<(ZExtUnit<{ ID }>, bool), &mut R> for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<(ZExtUnit<{ ID }>, bool), Self::Error> { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(&mut *reader) + } +} + +impl RCodec<(ZExtUnit<{ ID }>, bool), &mut R> for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, _reader: &mut R) -> Result<(ZExtUnit<{ ID }>, bool), Self::Error> { + if iext::eid(self.header) != ID { + return Err(DidntRead); + } + Ok((ZExtUnit::new(), has_flag(self.header, iext::FLAG_Z))) + } +} + +// ZExtZ64 +impl WCodec<(&ZExtZ64<{ ID }>, bool), &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: (&ZExtZ64<{ ID }>, bool)) -> Self::Output { + let (x, more) = x; + let mut header: u8 = ID; + if more { + header |= iext::FLAG_Z; + } + self.write(&mut *writer, header)?; + self.write(&mut *writer, x.value)?; + Ok(()) + } +} + +impl RCodec<(ZExtZ64<{ ID }>, bool), &mut R> for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<(ZExtZ64<{ ID }>, bool), Self::Error> { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(&mut *reader) + } +} + +impl RCodec<(ZExtZ64<{ ID }>, bool), &mut R> for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<(ZExtZ64<{ ID }>, bool), Self::Error> { + if iext::eid(self.header) != ID { + return Err(DidntRead); + } + + let value: u64 = self.codec.read(&mut *reader)?; + Ok((ZExtZ64::new(value), has_flag(self.header, iext::FLAG_Z))) + } +} + +// ZExtZBuf +impl WCodec<(&ZExtZBuf<{ ID }>, bool), &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: (&ZExtZBuf<{ ID }>, bool)) -> Self::Output { + let (x, more) = x; + let mut header: u8 = ID; + if more { + header |= iext::FLAG_Z; + } + self.write(&mut *writer, header)?; + let bodec = Zenoh080Bounded::::new(); + bodec.write(&mut *writer, &x.value)?; + Ok(()) + } +} + +impl RCodec<(ZExtZBuf<{ ID }>, bool), &mut R> for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<(ZExtZBuf<{ ID }>, bool), Self::Error> { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(&mut *reader) + } +} + +impl RCodec<(ZExtZBuf<{ ID }>, bool), &mut R> for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<(ZExtZBuf<{ ID }>, bool), Self::Error> { + if iext::eid(self.header) != ID { + return Err(DidntRead); + } + let bodec = Zenoh080Bounded::::new(); + let value: ZBuf = bodec.read(&mut *reader)?; + Ok((ZExtZBuf::new(value), has_flag(self.header, iext::FLAG_Z))) + } +} + +// ZExtZBufHeader +impl WCodec<(&ZExtZBufHeader<{ ID }>, bool), &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: (&ZExtZBufHeader<{ ID }>, bool)) -> Self::Output { + let (x, more) = x; + let mut header: u8 = ID; + if more { + header |= iext::FLAG_Z; + } + self.write(&mut *writer, header)?; + let bodec = Zenoh080Bounded::::new(); + bodec.write(&mut *writer, x.len)?; + Ok(()) + } +} + +impl RCodec<(ZExtZBufHeader<{ ID }>, bool), &mut R> for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<(ZExtZBufHeader<{ ID }>, bool), Self::Error> { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(&mut *reader) + } +} + +impl RCodec<(ZExtZBufHeader<{ ID }>, bool), &mut R> for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<(ZExtZBufHeader<{ ID }>, bool), Self::Error> { + if iext::eid(self.header) != ID { + return Err(DidntRead); + } + + let bodec = Zenoh080Bounded::::new(); + let len: usize = bodec.read(&mut *reader)?; + Ok(( + ZExtZBufHeader::new(len), + has_flag(self.header, iext::FLAG_Z), + )) + } +} + +// ZExtUnknown +impl WCodec<(&ZExtUnknown, bool), &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: (&ZExtUnknown, bool)) -> Self::Output { + let (x, more) = x; + let mut header: u8 = x.id; + if more { + header |= iext::FLAG_Z; + } + match &x.body { + ZExtBody::Unit => self.write(&mut *writer, header)?, + ZExtBody::Z64(u64) => { + self.write(&mut *writer, header)?; + self.write(&mut *writer, *u64)? + } + ZExtBody::ZBuf(zbuf) => { + self.write(&mut *writer, header)?; + let bodec = Zenoh080Bounded::::new(); + bodec.write(&mut *writer, zbuf)? + } + } + Ok(()) + } +} + +impl RCodec<(ZExtUnknown, bool), &mut R> for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<(ZExtUnknown, bool), Self::Error> { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(&mut *reader) + } +} + +impl RCodec<(ZExtUnknown, bool), &mut R> for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<(ZExtUnknown, bool), Self::Error> { + let body = match self.header & iext::ENC_MASK { + iext::ENC_UNIT => ZExtBody::Unit, + iext::ENC_Z64 => { + let u64: u64 = self.codec.read(&mut *reader)?; + ZExtBody::Z64(u64) + } + iext::ENC_ZBUF => { + let bodec = Zenoh080Bounded::::new(); + let zbuf: ZBuf = bodec.read(&mut *reader)?; + ZExtBody::ZBuf(zbuf) + } + _ => { + return Err(DidntRead); + } + }; + + Ok(( + ZExtUnknown { + id: self.header & !iext::FLAG_Z, + body, + }, + has_flag(self.header, iext::FLAG_Z), + )) + } +} + +impl WCodec<&[ZExtUnknown], &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &[ZExtUnknown]) -> Self::Output { + let len = x.len(); + for (i, e) in x.iter().enumerate() { + self.write(&mut *writer, (e, i < len - 1))?; + } + Ok(()) + } +} + +impl RCodec, &mut R> for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result, Self::Error> { + let mut exts = Vec::new(); + let mut has_ext = reader.can_read(); + while has_ext { + let (e, more): (ZExtUnknown, bool) = self.read(&mut *reader)?; + exts.push(e); + has_ext = more; + } + Ok(exts) + } +} + +// Macros +#[macro_export] +macro_rules! impl_zextz64 { + ($ext:ty, $id:expr) => { + impl WCodec<($ext, bool), &mut W> for Zenoh080 + where + W: Writer, + { + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: ($ext, bool)) -> Self::Output { + let (x, more) = x; + let ext: ZExtZ64<{ $id }> = x.into(); + self.write(&mut *writer, (&ext, more)) + } + } + + impl RCodec<($ext, bool), &mut R> for Zenoh080 + where + R: Reader, + { + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<($ext, bool), Self::Error> { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(reader) + } + } + + impl RCodec<($ext, bool), &mut R> for Zenoh080Header + where + R: Reader, + { + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<($ext, bool), Self::Error> { + let (ext, more): (ZExtZ64<{ $id }>, bool) = self.read(&mut *reader)?; + Ok((ext.into(), more)) + } + } + }; +} diff --git a/commons/zenoh-codec/src/common/mod.rs b/commons/zenoh-codec/src/common/mod.rs index 60d0e9c1a2..4c25c93241 100644 --- a/commons/zenoh-codec/src/common/mod.rs +++ b/commons/zenoh-codec/src/common/mod.rs @@ -11,5 +11,5 @@ // Contributors: // ZettaScale Zenoh Team, // -mod attachment; +pub mod extension; mod priority; diff --git a/commons/zenoh-codec/src/common/priority.rs b/commons/zenoh-codec/src/common/priority.rs index e323b95686..776229971e 100644 --- a/commons/zenoh-codec/src/common/priority.rs +++ b/commons/zenoh-codec/src/common/priority.rs @@ -11,15 +11,15 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::{RCodec, WCodec, Zenoh060, Zenoh060Header}; +use crate::{RCodec, WCodec, Zenoh080, Zenoh080Header}; use core::convert::TryInto; use zenoh_buffers::{ reader::{DidntRead, Reader}, writer::{DidntWrite, Writer}, }; -use zenoh_protocol::{common::imsg, core::Priority, transport::tmsg}; +use zenoh_protocol::{common::imsg, core::Priority}; -impl WCodec<&Priority, &mut W> for Zenoh060 +impl WCodec<&Priority, &mut W> for Zenoh080 where W: Writer, { @@ -27,28 +27,27 @@ where fn write(self, writer: &mut W, x: &Priority) -> Self::Output { // Header - let header = tmsg::id::PRIORITY | ((*x as u8) << imsg::HEADER_BITS); + let header = imsg::id::PRIORITY | ((*x as u8) << imsg::HEADER_BITS); self.write(&mut *writer, header)?; Ok(()) } } -impl RCodec for Zenoh060 +impl RCodec for Zenoh080 where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(reader) } } -impl RCodec for Zenoh060Header +impl RCodec for Zenoh080Header where R: Reader, { diff --git a/commons/zenoh-codec/src/core/encoding.rs b/commons/zenoh-codec/src/core/encoding.rs index a3509f50f4..478bcf1cd8 100644 --- a/commons/zenoh-codec/src/core/encoding.rs +++ b/commons/zenoh-codec/src/core/encoding.rs @@ -11,37 +11,45 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::{RCodec, WCodec, Zenoh060}; +use crate::{LCodec, RCodec, WCodec, Zenoh080, Zenoh080Bounded}; use alloc::string::String; use zenoh_buffers::{ reader::{DidntRead, Reader}, writer::{DidntWrite, Writer}, }; -use zenoh_protocol::core::{Encoding, ZInt}; +use zenoh_protocol::core::Encoding; -impl WCodec<&Encoding, &mut W> for Zenoh060 +impl LCodec<&Encoding> for Zenoh080 { + fn w_len(self, x: &Encoding) -> usize { + 1 + self.w_len(x.suffix()) + } +} + +impl WCodec<&Encoding, &mut W> for Zenoh080 where W: Writer, { type Output = Result<(), DidntWrite>; fn write(self, writer: &mut W, x: &Encoding) -> Self::Output { - self.write(&mut *writer, u8::from(*x.prefix()))?; - self.write(&mut *writer, x.suffix())?; + let zodec = Zenoh080Bounded::::new(); + zodec.write(&mut *writer, *x.prefix() as u8)?; + zodec.write(&mut *writer, x.suffix())?; Ok(()) } } -impl RCodec for Zenoh060 +impl RCodec for Zenoh080 where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - let prefix: ZInt = self.read(&mut *reader)?; - let suffix: String = self.read(&mut *reader)?; - let encoding = Encoding::new(prefix, suffix).ok_or(DidntRead)?; + let zodec = Zenoh080Bounded::::new(); + let prefix: u8 = zodec.read(&mut *reader)?; + let suffix: String = zodec.read(&mut *reader)?; + let encoding = Encoding::new(prefix, suffix).map_err(|_| DidntRead)?; Ok(encoding) } } diff --git a/commons/zenoh-codec/src/core/endpoint.rs b/commons/zenoh-codec/src/core/endpoint.rs deleted file mode 100644 index 95cf30334f..0000000000 --- a/commons/zenoh-codec/src/core/endpoint.rs +++ /dev/null @@ -1,75 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use crate::{RCodec, WCodec, Zenoh060}; -use alloc::{string::String, vec::Vec}; -use core::convert::TryFrom; -use zenoh_buffers::{ - reader::{DidntRead, Reader}, - writer::{DidntWrite, Writer}, -}; -use zenoh_protocol::core::EndPoint; - -impl WCodec<&EndPoint, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &EndPoint) -> Self::Output { - self.write(writer, x.as_str()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let loc: String = self.read(reader)?; - EndPoint::try_from(loc).map_err(|_| DidntRead) - } -} - -impl WCodec<&[EndPoint], &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &[EndPoint]) -> Self::Output { - self.write(&mut *writer, x.len())?; - for l in x { - self.write(&mut *writer, l)?; - } - Ok(()) - } -} - -impl RCodec, &mut R> for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result, Self::Error> { - let len = self.read(&mut *reader)?; - let mut vec: Vec = Vec::with_capacity(len); - for _ in 0..len { - vec.push(self.read(&mut *reader)?); - } - Ok(vec) - } -} diff --git a/commons/zenoh-codec/src/core/locator.rs b/commons/zenoh-codec/src/core/locator.rs index a128744a6d..0bbd28a189 100644 --- a/commons/zenoh-codec/src/core/locator.rs +++ b/commons/zenoh-codec/src/core/locator.rs @@ -11,7 +11,7 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::{RCodec, WCodec, Zenoh060}; +use crate::{RCodec, WCodec, Zenoh080, Zenoh080Bounded}; use alloc::{string::String, vec::Vec}; use core::convert::TryFrom; use zenoh_buffers::{ @@ -20,30 +20,32 @@ use zenoh_buffers::{ }; use zenoh_protocol::core::Locator; -impl WCodec<&Locator, &mut W> for Zenoh060 +impl WCodec<&Locator, &mut W> for Zenoh080 where W: Writer, { type Output = Result<(), DidntWrite>; fn write(self, writer: &mut W, x: &Locator) -> Self::Output { - self.write(writer, x.as_str()) + let zodec = Zenoh080Bounded::::new(); + zodec.write(writer, x.as_str()) } } -impl RCodec for Zenoh060 +impl RCodec for Zenoh080 where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - let loc: String = self.read(reader)?; + let zodec = Zenoh080Bounded::::new(); + let loc: String = zodec.read(reader)?; Locator::try_from(loc).map_err(|_| DidntRead) } } -impl WCodec<&[Locator], &mut W> for Zenoh060 +impl WCodec<&[Locator], &mut W> for Zenoh080 where W: Writer, { @@ -58,7 +60,7 @@ where } } -impl RCodec, &mut R> for Zenoh060 +impl RCodec, &mut R> for Zenoh080 where R: Reader, { diff --git a/commons/zenoh-codec/src/core/mod.rs b/commons/zenoh-codec/src/core/mod.rs index b6ebcd404e..1f48def695 100644 --- a/commons/zenoh-codec/src/core/mod.rs +++ b/commons/zenoh-codec/src/core/mod.rs @@ -12,122 +12,253 @@ // ZettaScale Zenoh Team, // mod encoding; -mod endpoint; -mod keyexpr; mod locator; mod property; +#[cfg(feature = "shared-memory")] +mod shm; mod timestamp; +mod wire_expr; mod zbuf; mod zenohid; mod zint; mod zslice; -use crate::{RCodec, WCodec, Zenoh060}; +use crate::{LCodec, RCodec, WCodec, Zenoh080, Zenoh080Bounded}; use alloc::{string::String, vec::Vec}; use zenoh_buffers::{ reader::{DidntRead, Reader}, writer::{DidntWrite, Writer}, }; -// u8 -impl WCodec for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; +// [u8; N] +macro_rules! array_impl { + ($n:expr) => { + impl WCodec<[u8; $n], &mut W> for Zenoh080 + where + W: Writer, + { + type Output = Result<(), DidntWrite>; - fn write(self, writer: &mut W, x: u8) -> Self::Output { - writer.write_u8(x) - } + fn write(self, writer: &mut W, x: [u8; $n]) -> Self::Output { + writer.write_exact(x.as_slice()) + } + } + + impl WCodec<&[u8; $n], &mut W> for Zenoh080 + where + W: Writer, + { + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &[u8; $n]) -> Self::Output { + self.write(writer, *x) + } + } + + impl RCodec<[u8; $n], &mut R> for Zenoh080 + where + R: Reader, + { + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<[u8; $n], Self::Error> { + let mut x = [0u8; $n]; + reader.read_exact(&mut x)?; + Ok(x) + } + } + + impl LCodec<[u8; $n]> for Zenoh080 { + fn w_len(self, _: [u8; $n]) -> usize { + $n + } + } + }; } -impl WCodec<&u8, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; +array_impl!(1); +array_impl!(2); +array_impl!(3); +array_impl!(4); +array_impl!(5); +array_impl!(6); +array_impl!(7); +array_impl!(8); +array_impl!(9); +array_impl!(10); +array_impl!(11); +array_impl!(12); +array_impl!(13); +array_impl!(14); +array_impl!(15); +array_impl!(16); - fn write(self, writer: &mut W, x: &u8) -> Self::Output { - self.write(writer, *x) - } +// &[u8] / Vec - Bounded +macro_rules! vec_impl { + ($bound:ty) => { + impl WCodec<&[u8], &mut W> for Zenoh080Bounded<$bound> + where + W: Writer, + { + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &[u8]) -> Self::Output { + self.write(&mut *writer, x.len())?; + if x.is_empty() { + Ok(()) + } else { + writer.write_exact(x) + } + } + } + + impl RCodec, &mut R> for Zenoh080Bounded<$bound> + where + R: Reader, + { + type Error = DidntRead; + + #[allow(clippy::uninit_vec)] + fn read(self, reader: &mut R) -> Result, Self::Error> { + let len: usize = self.read(&mut *reader)?; + let mut buff = zenoh_buffers::vec::uninit(len); + if len != 0 { + reader.read_exact(&mut buff[..])?; + } + Ok(buff) + } + } + }; } -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; +vec_impl!(u8); +vec_impl!(u16); +vec_impl!(u32); +vec_impl!(u64); +vec_impl!(usize); - fn read(self, reader: &mut R) -> Result { - reader.read_u8() +// &[u8] / Vec +impl LCodec<&[u8]> for Zenoh080 { + fn w_len(self, x: &[u8]) -> usize { + self.w_len(x.len()) + x.len() } } -// &[u8] / Vec -impl WCodec<&[u8], &mut W> for Zenoh060 +impl WCodec<&[u8], &mut W> for Zenoh080 where W: Writer, { type Output = Result<(), DidntWrite>; fn write(self, writer: &mut W, x: &[u8]) -> Self::Output { - self.write(&mut *writer, x.len())?; - if x.is_empty() { - Ok(()) - } else { - writer.write_exact(x) - } + let zcodec = Zenoh080Bounded::::new(); + zcodec.write(&mut *writer, x) } } -impl RCodec, &mut R> for Zenoh060 +impl RCodec, &mut R> for Zenoh080 where R: Reader, { type Error = DidntRead; - #[allow(clippy::uninit_vec)] fn read(self, reader: &mut R) -> Result, Self::Error> { - let len: usize = self.read(&mut *reader)?; - let mut buff = zenoh_buffers::vec::uninit(len); - if len != 0 { - reader.read_exact(&mut buff[..])?; - } - Ok(buff) + let zcodec = Zenoh080Bounded::::new(); + zcodec.read(&mut *reader) } } +// &str / String - Bounded +macro_rules! str_impl { + ($bound:ty) => { + impl WCodec<&str, &mut W> for Zenoh080Bounded<$bound> + where + W: Writer, + { + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &str) -> Self::Output { + self.write(&mut *writer, x.as_bytes()) + } + } + + impl WCodec<&String, &mut W> for Zenoh080Bounded<$bound> + where + W: Writer, + { + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &String) -> Self::Output { + self.write(&mut *writer, x.as_str()) + } + } + + impl RCodec for Zenoh080Bounded<$bound> + where + R: Reader, + { + type Error = DidntRead; + + #[allow(clippy::uninit_vec)] + fn read(self, reader: &mut R) -> Result { + let vec: Vec = self.read(&mut *reader)?; + String::from_utf8(vec).map_err(|_| DidntRead) + } + } + }; +} + +str_impl!(u8); +str_impl!(u16); +str_impl!(u32); +str_impl!(u64); +str_impl!(usize); + // &str / String -impl WCodec<&str, &mut W> for Zenoh060 +impl LCodec<&str> for Zenoh080 { + fn w_len(self, x: &str) -> usize { + self.w_len(x.as_bytes()) + } +} + +impl LCodec<&String> for Zenoh080 { + fn w_len(self, x: &String) -> usize { + self.w_len(x.as_bytes()) + } +} + +impl WCodec<&str, &mut W> for Zenoh080 where W: Writer, { type Output = Result<(), DidntWrite>; fn write(self, writer: &mut W, x: &str) -> Self::Output { - self.write(&mut *writer, x.as_bytes()) + let zcodec = Zenoh080Bounded::::new(); + zcodec.write(&mut *writer, x) } } -impl WCodec<&String, &mut W> for Zenoh060 +impl WCodec<&String, &mut W> for Zenoh080 where W: Writer, { type Output = Result<(), DidntWrite>; fn write(self, writer: &mut W, x: &String) -> Self::Output { - self.write(&mut *writer, x.as_str()) + let zcodec = Zenoh080Bounded::::new(); + zcodec.write(&mut *writer, x) } } -impl RCodec for Zenoh060 +impl RCodec for Zenoh080 where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - let vec: Vec = self.read(&mut *reader)?; - String::from_utf8(vec).map_err(|_| DidntRead) + let zcodec = Zenoh080Bounded::::new(); + zcodec.read(&mut *reader) } } diff --git a/commons/zenoh-codec/src/core/property.rs b/commons/zenoh-codec/src/core/property.rs index 17fa11b288..02536ccd82 100644 --- a/commons/zenoh-codec/src/core/property.rs +++ b/commons/zenoh-codec/src/core/property.rs @@ -11,15 +11,15 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::{RCodec, WCodec, Zenoh060}; +use crate::{RCodec, WCodec, Zenoh080}; use alloc::vec::Vec; use zenoh_buffers::{ reader::{DidntRead, Reader}, writer::{DidntWrite, Writer}, }; -use zenoh_protocol::core::{Property, ZInt}; +use zenoh_protocol::core::Property; -impl WCodec<&Property, &mut W> for Zenoh060 +impl WCodec<&Property, &mut W> for Zenoh080 where W: Writer, { @@ -32,21 +32,21 @@ where } } -impl RCodec for Zenoh060 +impl RCodec for Zenoh080 where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - let key: ZInt = self.read(&mut *reader)?; + let key: u64 = self.read(&mut *reader)?; let value: Vec = self.read(&mut *reader)?; Ok(Property { key, value }) } } -impl WCodec<&[Property], &mut W> for Zenoh060 +impl WCodec<&[Property], &mut W> for Zenoh080 where W: Writer, { @@ -62,7 +62,7 @@ where } } -impl RCodec, &mut R> for Zenoh060 +impl RCodec, &mut R> for Zenoh080 where R: Reader, { diff --git a/commons/zenoh-codec/src/core/shm.rs b/commons/zenoh-codec/src/core/shm.rs new file mode 100644 index 0000000000..1ab6976ebe --- /dev/null +++ b/commons/zenoh-codec/src/core/shm.rs @@ -0,0 +1,51 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::{RCodec, WCodec, Zenoh080}; +use zenoh_buffers::{ + reader::{DidntRead, Reader}, + writer::{DidntWrite, Writer}, +}; +use zenoh_shm::SharedMemoryBufInfo; + +impl WCodec<&SharedMemoryBufInfo, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &SharedMemoryBufInfo) -> Self::Output { + self.write(&mut *writer, x.offset)?; + self.write(&mut *writer, x.length)?; + self.write(&mut *writer, x.shm_manager.as_str())?; + self.write(&mut *writer, x.kind)?; + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let offset: usize = self.read(&mut *reader)?; + let length: usize = self.read(&mut *reader)?; + let shm_manager: String = self.read(&mut *reader)?; + let kind: u8 = self.read(&mut *reader)?; + + let shm_info = SharedMemoryBufInfo::new(offset, length, shm_manager, kind); + Ok(shm_info) + } +} diff --git a/commons/zenoh-codec/src/core/timestamp.rs b/commons/zenoh-codec/src/core/timestamp.rs index 9f2d445d1c..4891643192 100644 --- a/commons/zenoh-codec/src/core/timestamp.rs +++ b/commons/zenoh-codec/src/core/timestamp.rs @@ -11,7 +11,7 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::{RCodec, WCodec, Zenoh060}; +use crate::{LCodec, RCodec, WCodec, Zenoh080}; use core::convert::TryFrom; use zenoh_buffers::{ reader::{DidntRead, Reader}, @@ -19,7 +19,13 @@ use zenoh_buffers::{ }; use zenoh_protocol::core::{Timestamp, ZenohId}; -impl WCodec<&Timestamp, &mut W> for Zenoh060 +impl LCodec<&Timestamp> for Zenoh080 { + fn w_len(self, x: &Timestamp) -> usize { + self.w_len(x.get_time().as_u64()) + self.w_len(x.get_id().size()) + } +} + +impl WCodec<&Timestamp, &mut W> for Zenoh080 where W: Writer, { @@ -33,7 +39,7 @@ where } } -impl RCodec for Zenoh060 +impl RCodec for Zenoh080 where R: Reader, { diff --git a/commons/zenoh-codec/src/core/keyexpr.rs b/commons/zenoh-codec/src/core/wire_expr.rs similarity index 59% rename from commons/zenoh-codec/src/core/keyexpr.rs rename to commons/zenoh-codec/src/core/wire_expr.rs index d13261bb1b..bc484149ce 100644 --- a/commons/zenoh-codec/src/core/keyexpr.rs +++ b/commons/zenoh-codec/src/core/wire_expr.rs @@ -11,45 +11,55 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::{RCodec, WCodec, Zenoh060, Zenoh060Condition}; +use crate::{core::Zenoh080Bounded, RCodec, WCodec, Zenoh080, Zenoh080Condition}; use alloc::string::String; use zenoh_buffers::{ reader::{DidntRead, Reader}, writer::{DidntWrite, Writer}, }; -use zenoh_protocol::core::{WireExpr, ZInt}; +use zenoh_protocol::{ + core::{ExprId, ExprLen, WireExpr}, + network::Mapping, +}; -impl WCodec<&WireExpr<'_>, &mut W> for Zenoh060 +impl WCodec<&WireExpr<'_>, &mut W> for Zenoh080 where W: Writer, { type Output = Result<(), DidntWrite>; fn write(self, writer: &mut W, x: &WireExpr<'_>) -> Self::Output { - self.write(&mut *writer, x.scope)?; + let zodec = Zenoh080Bounded::::new(); + zodec.write(&mut *writer, x.scope)?; + if x.has_suffix() { - self.write(&mut *writer, x.suffix.as_ref())?; + let zodec = Zenoh080Bounded::::new(); + zodec.write(&mut *writer, x.suffix.as_ref())?; } Ok(()) } } -impl RCodec, &mut R> for Zenoh060Condition +impl RCodec, &mut R> for Zenoh080Condition where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result, Self::Error> { - let scope: ZInt = self.codec.read(&mut *reader)?; + let zodec = Zenoh080Bounded::::new(); + let scope: ExprId = zodec.read(&mut *reader)?; + let suffix: String = if self.condition { - self.codec.read(&mut *reader)? + let zodec = Zenoh080Bounded::::new(); + zodec.read(&mut *reader)? } else { String::new() }; Ok(WireExpr { scope, suffix: suffix.into(), + mapping: Mapping::default(), }) } } diff --git a/commons/zenoh-codec/src/core/zbuf.rs b/commons/zenoh-codec/src/core/zbuf.rs index 54f8439704..ccf5d595ce 100644 --- a/commons/zenoh-codec/src/core/zbuf.rs +++ b/commons/zenoh-codec/src/core/zbuf.rs @@ -11,144 +11,175 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::{RCodec, WCodec, Zenoh060}; +use crate::{LCodec, RCodec, WCodec, Zenoh080, Zenoh080Bounded}; use zenoh_buffers::{ reader::{DidntRead, Reader}, writer::{DidntWrite, Writer}, SplitBuffer, ZBuf, }; -#[cfg(feature = "shared-memory")] -use { - crate::Zenoh060Condition, core::any::TypeId, zenoh_buffers::ZSlice, - zenoh_shm::SharedMemoryBufInfoSerialized, -}; - -// ZBuf flat -impl WCodec<&ZBuf, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - fn write(self, writer: &mut W, x: &ZBuf) -> Self::Output { - self.write(&mut *writer, x.len())?; - for s in x.zslices() { - writer.write_zslice(s)?; +// ZBuf bounded +macro_rules! zbuf_impl { + ($bound:ty) => { + impl LCodec<&ZBuf> for Zenoh080Bounded<$bound> { + fn w_len(self, message: &ZBuf) -> usize { + message.len() + } } - Ok(()) - } -} -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; + impl WCodec<&ZBuf, &mut W> for Zenoh080Bounded<$bound> + where + W: Writer, + { + type Output = Result<(), DidntWrite>; - fn read(self, reader: &mut R) -> Result { - let len: usize = self.read(&mut *reader)?; - let mut zbuf = ZBuf::default(); - reader.read_zslices(len, |s| zbuf.push_zslice(s))?; - Ok(zbuf) - } -} + fn write(self, writer: &mut W, x: &ZBuf) -> Self::Output { + self.write(&mut *writer, x.len())?; + for s in x.zslices() { + writer.write_zslice(s)?; + } + Ok(()) + } + } -// ZBuf sliced -#[cfg(feature = "shared-memory")] -#[derive(Default)] -struct Zenoh060Sliced { - codec: Zenoh060, + impl RCodec for Zenoh080Bounded<$bound> + where + R: Reader, + { + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let len: usize = self.read(&mut *reader)?; + let mut zbuf = ZBuf::empty(); + reader.read_zslices(len, |s| zbuf.push_zslice(s))?; + Ok(zbuf) + } + } + }; } -#[cfg(feature = "shared-memory")] -impl WCodec<&ZBuf, &mut W> for Zenoh060Sliced +zbuf_impl!(u8); +zbuf_impl!(u16); +zbuf_impl!(u32); +zbuf_impl!(u64); +zbuf_impl!(usize); + +// ZBuf flat +impl WCodec<&ZBuf, &mut W> for Zenoh080 where W: Writer, { type Output = Result<(), DidntWrite>; fn write(self, writer: &mut W, x: &ZBuf) -> Self::Output { - self.codec.write(&mut *writer, x.zslices().count())?; - - for zs in x.zslices() { - if zs.buf.as_any().type_id() == TypeId::of::() { - self.codec - .write(&mut *writer, super::zslice::kind::SHM_INFO)?; - } else { - self.codec.write(&mut *writer, super::zslice::kind::RAW)?; - } - - self.codec.write(&mut *writer, zs)?; - } - - Ok(()) + let zodec = Zenoh080Bounded::::new(); + zodec.write(&mut *writer, x) } } -#[cfg(feature = "shared-memory")] -impl RCodec for Zenoh060Sliced +impl RCodec for Zenoh080 where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - let num: usize = self.codec.read(&mut *reader)?; - let mut zbuf = ZBuf::default(); - for _ in 0..num { - let kind: u8 = self.codec.read(&mut *reader)?; - match kind { - super::zslice::kind::RAW => { - let len: usize = self.codec.read(&mut *reader)?; - reader.read_zslices(len, |s| zbuf.push_zslice(s))?; - } - super::zslice::kind::SHM_INFO => { - let bytes: Vec = self.codec.read(&mut *reader)?; - let shm_info: SharedMemoryBufInfoSerialized = bytes.into(); - let zslice: ZSlice = shm_info.into(); - zbuf.push_zslice(zslice); - } - _ => return Err(DidntRead), - } - } - Ok(zbuf) + let zodec = Zenoh080Bounded::::new(); + zodec.read(&mut *reader) } } -#[cfg(feature = "shared-memory")] -impl WCodec<&ZBuf, &mut W> for Zenoh060Condition -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &ZBuf) -> Self::Output { - let is_sliced = self.condition; - - if is_sliced { - let codec = Zenoh060Sliced::default(); - codec.write(&mut *writer, x) - } else { - self.codec.write(&mut *writer, x) - } +impl LCodec<&ZBuf> for Zenoh080 { + fn w_len(self, message: &ZBuf) -> usize { + let zodec = Zenoh080Bounded::::new(); + zodec.w_len(message) } } +// ZBuf sliced #[cfg(feature = "shared-memory")] -impl RCodec for Zenoh060Condition -where - R: Reader, -{ - type Error = DidntRead; +mod shm { + use super::*; + use crate::Zenoh080Sliced; + use zenoh_buffers::{ZSlice, ZSliceKind}; + + const RAW: u8 = 0; + const SHM_PTR: u8 = 1; + + macro_rules! zbuf_sliced_impl { + ($bound:ty) => { + impl LCodec<&ZBuf> for Zenoh080Sliced<$bound> { + fn w_len(self, message: &ZBuf) -> usize { + if self.is_sliced { + message.zslices().fold(0, |acc, x| acc + 1 + x.len()) + } else { + self.codec.w_len(message) + } + } + } - fn read(self, reader: &mut R) -> Result { - let is_sliced = self.condition; + impl WCodec<&ZBuf, &mut W> for Zenoh080Sliced<$bound> + where + W: Writer, + { + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &ZBuf) -> Self::Output { + if self.is_sliced { + self.codec.write(&mut *writer, x.zslices().count())?; + + for zs in x.zslices() { + match zs.kind { + ZSliceKind::Raw => self.codec.write(&mut *writer, RAW)?, + ZSliceKind::ShmPtr => self.codec.write(&mut *writer, SHM_PTR)?, + } + self.codec.write(&mut *writer, zs)?; + } + } else { + self.codec.write(&mut *writer, x)?; + } + + Ok(()) + } + } - if is_sliced { - let codec = Zenoh060Sliced::default(); - codec.read(&mut *reader) - } else { - self.codec.read(&mut *reader) - } + impl RCodec for Zenoh080Sliced<$bound> + where + R: Reader, + { + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + if self.is_sliced { + let num: usize = self.codec.read(&mut *reader)?; + let mut zbuf = ZBuf::empty(); + for _ in 0..num { + let kind: u8 = self.codec.read(&mut *reader)?; + match kind { + RAW => { + let len: usize = self.codec.read(&mut *reader)?; + reader.read_zslices(len, |s| zbuf.push_zslice(s))?; + } + SHM_PTR => { + let mut zslice: ZSlice = self.codec.read(&mut *reader)?; + zslice.kind = ZSliceKind::ShmPtr; + zbuf.push_zslice(zslice); + } + _ => return Err(DidntRead), + } + } + Ok(zbuf) + } else { + self.codec.read(&mut *reader) + } + } + } + }; } + + zbuf_sliced_impl!(u8); + zbuf_sliced_impl!(u16); + zbuf_sliced_impl!(u32); + zbuf_sliced_impl!(u64); + zbuf_sliced_impl!(usize); } diff --git a/commons/zenoh-codec/src/core/zenohid.rs b/commons/zenoh-codec/src/core/zenohid.rs index 32c1196a14..6c53d4e63f 100644 --- a/commons/zenoh-codec/src/core/zenohid.rs +++ b/commons/zenoh-codec/src/core/zenohid.rs @@ -11,7 +11,7 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::{RCodec, WCodec, Zenoh060}; +use crate::{LCodec, RCodec, WCodec, Zenoh080, Zenoh080Length}; use core::convert::TryFrom; use zenoh_buffers::{ reader::{DidntRead, Reader}, @@ -19,7 +19,13 @@ use zenoh_buffers::{ }; use zenoh_protocol::core::ZenohId; -impl WCodec<&ZenohId, &mut W> for Zenoh060 +impl LCodec<&ZenohId> for Zenoh080 { + fn w_len(self, x: &ZenohId) -> usize { + x.size() + } +} + +impl WCodec<&ZenohId, &mut W> for Zenoh080 where W: Writer, { @@ -30,7 +36,7 @@ where } } -impl RCodec for Zenoh060 +impl RCodec for Zenoh080 where R: Reader, { @@ -41,9 +47,38 @@ where if size > ZenohId::MAX_SIZE { return Err(DidntRead); } - let mut id = [0; ZenohId::MAX_SIZE]; reader.read_exact(&mut id[..size])?; ZenohId::try_from(&id[..size]).map_err(|_| DidntRead) } } + +impl WCodec<&ZenohId, &mut W> for Zenoh080Length +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &ZenohId) -> Self::Output { + if self.length > ZenohId::MAX_SIZE { + return Err(DidntWrite); + } + writer.write_exact(&x.to_le_bytes()[..x.size()]) + } +} + +impl RCodec for Zenoh080Length +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + if self.length > ZenohId::MAX_SIZE { + return Err(DidntRead); + } + let mut id = [0; ZenohId::MAX_SIZE]; + reader.read_exact(&mut id[..self.length])?; + ZenohId::try_from(&id[..self.length]).map_err(|_| DidntRead) + } +} diff --git a/commons/zenoh-codec/src/core/zint.rs b/commons/zenoh-codec/src/core/zint.rs index 1503ab6fd8..1c2f5a28e4 100644 --- a/commons/zenoh-codec/src/core/zint.rs +++ b/commons/zenoh-codec/src/core/zint.rs @@ -11,8 +11,7 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::{RCodec, WCodec, Zenoh060}; -use core::convert::TryInto; +use crate::{LCodec, RCodec, WCodec, Zenoh080, Zenoh080Bounded}; use zenoh_buffers::{ reader::{DidntRead, Reader}, writer::{DidntWrite, Writer}, @@ -20,8 +19,91 @@ use zenoh_buffers::{ const VLE_LEN: usize = 10; -// ZInt -impl WCodec for Zenoh060 +impl LCodec for Zenoh080 { + fn w_len(self, x: u64) -> usize { + const B1: u64 = u64::MAX << 7; + const B2: u64 = u64::MAX << (7 * 2); + const B3: u64 = u64::MAX << (7 * 3); + const B4: u64 = u64::MAX << (7 * 4); + const B5: u64 = u64::MAX << (7 * 5); + const B6: u64 = u64::MAX << (7 * 6); + const B7: u64 = u64::MAX << (7 * 7); + const B8: u64 = u64::MAX << (7 * 8); + const B9: u64 = u64::MAX << (7 * 9); + + if (x & B1) == 0 { + 1 + } else if (x & B2) == 0 { + 2 + } else if (x & B3) == 0 { + 3 + } else if (x & B4) == 0 { + 4 + } else if (x & B5) == 0 { + 5 + } else if (x & B6) == 0 { + 6 + } else if (x & B7) == 0 { + 7 + } else if (x & B8) == 0 { + 8 + } else if (x & B9) == 0 { + 9 + } else { + 10 + } + } +} + +impl LCodec for Zenoh080 { + fn w_len(self, x: usize) -> usize { + self.w_len(x as u64) + } +} + +impl LCodec for Zenoh080 { + fn w_len(self, x: u32) -> usize { + self.w_len(x as u64) + } +} + +impl LCodec for Zenoh080 { + fn w_len(self, x: u16) -> usize { + self.w_len(x as u64) + } +} + +impl LCodec for Zenoh080 { + fn w_len(self, _: u8) -> usize { + 1 + } +} + +// u8 +impl WCodec for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: u8) -> Self::Output { + writer.write_u8(x) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + reader.read_u8() + } +} + +// u64 +impl WCodec for Zenoh080 where W: Writer, { @@ -44,18 +126,7 @@ where } } -impl WCodec<&u64, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &u64) -> Self::Output { - self.write(writer, *x) - } -} - -impl RCodec for Zenoh060 +impl RCodec for Zenoh080 where R: Reader, { @@ -82,40 +153,208 @@ where } } -// usize -impl WCodec for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; +// Derive impls +macro_rules! uint_impl { + ($uint:ty) => { + impl WCodec<$uint, &mut W> for Zenoh080 + where + W: Writer, + { + type Output = Result<(), DidntWrite>; - fn write(self, writer: &mut W, x: usize) -> Self::Output { - let x: u64 = x.try_into().map_err(|_| DidntWrite)?; - self.write(writer, x) - } + fn write(self, writer: &mut W, x: $uint) -> Self::Output { + self.write(writer, x as u64) + } + } + + impl RCodec<$uint, &mut R> for Zenoh080 + where + R: Reader, + { + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<$uint, Self::Error> { + let x: u64 = self.read(reader)?; + Ok(x as $uint) + } + } + }; } -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; +uint_impl!(u16); +uint_impl!(u32); +uint_impl!(usize); - fn read(self, reader: &mut R) -> Result { - let x: u64 = >::read(self, reader)?; - x.try_into().map_err(|_| DidntRead) - } +macro_rules! uint_ref_impl { + ($uint:ty) => { + impl WCodec<&$uint, &mut W> for Zenoh080 + where + W: Writer, + { + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &$uint) -> Self::Output { + self.write(writer, *x) + } + } + }; +} + +uint_ref_impl!(u8); +uint_ref_impl!(u16); +uint_ref_impl!(u32); +uint_ref_impl!(u64); +uint_ref_impl!(usize); + +// Encode unsigned integer and verify that the size boundaries are respected +macro_rules! zint_impl_codec { + ($zint:ty, $bound:ty) => { + impl WCodec<$zint, &mut W> for Zenoh080Bounded<$bound> + where + W: Writer, + { + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: $zint) -> Self::Output { + if (x as u64 & !(<$bound>::MAX as u64)) != 0 { + return Err(DidntWrite); + } + Zenoh080.write(writer, x as u64) + } + } + + impl RCodec<$zint, &mut R> for Zenoh080Bounded<$bound> + where + R: Reader, + { + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<$zint, Self::Error> { + let x: u64 = Zenoh080.read(reader)?; + if (x & !(<$bound>::MAX as u64)) != 0 { + return Err(DidntRead); + } + Ok(x as $zint) + } + } + }; +} +macro_rules! zint_impl { + ($zint:ty) => { + zint_impl_codec!($zint, u8); + zint_impl_codec!($zint, u16); + zint_impl_codec!($zint, u32); + zint_impl_codec!($zint, u64); + zint_impl_codec!($zint, usize); + }; } +zint_impl!(u8); +zint_impl!(u16); +zint_impl!(u32); +zint_impl!(u64); +zint_impl!(usize); + +// const MAX_LEN: usize = 9; +// const VLE_THR: u64 = 0xf8; // 248 +// impl WCodec for Zenoh080 +// where +// W: Writer, +// { +// type Output = Result<(), DidntWrite>; +// fn write(self, writer: &mut W, mut x: u64) -> Self::Output { +// writer.with_slot(MAX_LEN, move |into| { +// if x < VLE_THR { +// into[0] = x as u8; +// return 1; +// } +// x -= VLE_THR - 1; +// // SAFETY +// // The `if x < VLE_THR` check at the beginning followed by `x -= VLE_THR - 1` +// // guarantees at this point that `x` is never `0`. Since `x` is 64bit, +// // then `n` is guaranteed to have a value between 1 and 8, both inclusives. +// // `into` is guaranteed to be exactly 9 bytes long. Therefore, copying at most +// // 8 bytes with a pointer offest of 1 is actually safe. +// let n = 8 - (x.leading_zeros() / 8) as usize; +// unsafe { +// core::ptr::copy_nonoverlapping( +// x.to_le_bytes().as_ptr(), +// into.as_mut_ptr().offset(1), +// n, +// ) +// } +// into[0] = VLE_THR as u8 | (n - 1) as u8; +// 1 + n +// }) +// } +// } + +// impl RCodec for Zenoh080 +// where +// R: Reader, +// { +// type Error = DidntRead; +// fn read(self, reader: &mut R) -> Result { +// let b = reader.read_u8()?; +// if b < (VLE_THR as u8) { +// return Ok(b as u64); +// } +// let n = (1 + (b & !VLE_THR as u8)) as usize; +// let mut u64: [u8; 8] = 0u64.to_le_bytes(); +// reader.read_exact(&mut u64[0..n])?; +// let u64 = u64::from_le_bytes(u64); +// Ok(u64.saturating_add(VLE_THR - 1)) +// } +// } + +// mod tests { +// #[test] +// fn u64_overhead() { +// use crate::{WCodec, Zenoh080}; +// use zenoh_buffers::{ +// reader::{HasReader, Reader}, +// writer::HasWriter, +// }; + +// fn overhead(x: u64) -> usize { +// let codec = Zenoh080::new(); +// let mut b = vec![]; +// let mut w = b.writer(); +// codec.write(&mut w, x).unwrap(); +// let r = b.reader().remaining(); +// println!("{} {}", x, r); +// r +// } + +// assert_eq!(overhead(247), 1); +// assert_eq!(overhead(248), 2); +// assert_eq!(overhead(502), 2); +// assert_eq!(overhead(503), 3); +// assert_eq!(overhead(65_782), 3); +// assert_eq!(overhead(65_783), 4); +// assert_eq!(overhead(16_777_462), 4); +// assert_eq!(overhead(16_777_463), 5); +// assert_eq!(overhead(4_294_967_542), 5); +// assert_eq!(overhead(4_294_967_543), 6); +// assert_eq!(overhead(1_099_511_628_022), 6); +// assert_eq!(overhead(1_099_511_628_023), 7); +// assert_eq!(overhead(281_474_976_710_902), 7); +// assert_eq!(overhead(281_474_976_710_903), 8); +// assert_eq!(overhead(72_057_594_037_928_182), 8); +// assert_eq!(overhead(72_057_594_037_928_183), 9); +// assert_eq!(overhead(u64::MAX), 9); +// } +// } + // macro_rules! non_zero_array { // ($($i: expr,)*) => { // [$(match NonZeroU8::new($i) {Some(x) => x, None => panic!("Attempted to place 0 in an array of non-zeros litteral")}),*] // }; // } -// impl Zenoh070 { +// impl Zenoh080 { // pub const fn preview_length(&self, x: u64) -> NonZeroU8 { -// let x = match core::num::NonZeroU64::new(x) { +// let x = match NonZeroU64::new(x) { // Some(x) => x, // None => { // return unsafe { NonZeroU8::new_unchecked(1) }; @@ -131,197 +370,161 @@ where // } // } -// macro_rules! impl_unsigned_codec { -// ($t: ty) => { -// impl WCodec for Codec -// where -// Codec: WCodec, -// { -// type Output = >::Output; -// fn write(self, writer: Buffer, x: $t) -> Self::Output { -// self.write(writer, x as u64) -// } -// } - -// impl RCodec for Codec -// where -// Codec: RCodec, -// { -// type Error = ConversionOrReadError>::Error>; - -// fn read(self, writer: Reader) -> Result<$t, Self::Error> { -// match self.read(writer) { -// Ok(v) => v -// .try_into() -// .map_err(|e| ConversionOrReadError::ConversionError(e)), -// Err(e) => Err(ConversionOrReadError::ReadError(e)), -// } -// } -// } -// }; -// } - -// impl_unsigned_codec!(usize); -// impl_unsigned_codec!(u32); -// impl_unsigned_codec!(u16); -// impl_unsigned_codec!(u8); - -// #[derive(Debug, Clone)] -// pub enum ConversionOrReadError -// where -// T: TryInto, -// >::Error: core::fmt::Debug + Clone, -// { -// ReadError(E), -// ConversionError(>::Error), -// } - -// impl WCodec<&mut W, u64> for &Zenoh070 +// impl WCodec for Zenoh080 // where // W: Writer, // { // type Output = Result<(), DidntWrite>; // fn write(self, writer: &mut W, x: u64) -> Self::Output { +// const VLE_LEN: usize = 9; +// const VLE_MASK: [u8; VLE_LEN] = [ +// 0b11111111, // This is padding to avoid needless subtractions on index access +// 0, 0b00000001, 0b00000011, 0b00000111, 0b00001111, 0b00011111, 0b00111111, 0b01111111, +// ]; // const VLE_SHIFT: [u8; 65] = [ // 1, // This is padding to avoid needless subtractions on index access // 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, // 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 0, 0, // 0, 0, 0, 0, 0, 0, // ]; -// const VLE_MASK: [u8; 9] = [ -// 0b11111111, // This is padding to avoid needless subtractions on index access -// 0, 0b00000001, 0b00000011, 0b00000111, 0b00001111, 0b00011111, 0b00111111, 0b01111111, -// ]; -// const VLE_LEN: usize = 9; -// writer.with_slot(VLE_LEN, move |mut buffer| { + +// writer.with_slot(VLE_LEN, move |buffer| { // // since leading_zeros will jump conditionally on 0 anyway (`asm {bsr 0}` is UB), might as well jump to return -// let x = match core::num::NonZeroU64::new(x) { +// let x = match NonZeroU64::new(x) { // Some(x) => x, // None => { // buffer[0] = 0; // return 1; // } // }; + // let needed_bits = u64::BITS - x.leading_zeros(); // let payload_size = VLE_SHIFT[needed_bits as usize]; // let shift_payload = payload_size == 0; // let mut x: u64 = x.into(); // x <<= payload_size; // let serialized = x.to_le_bytes(); + // unsafe { -// core::ptr::copy_nonoverlapping( +// ptr::copy_nonoverlapping( // serialized.as_ptr(), // buffer.as_mut_ptr().offset(shift_payload as isize), // u64::BITS as usize / 8, // ) // }; + // let needed_bytes = payload_size as usize; // buffer[0] |= VLE_MASK[needed_bytes]; -// let to_write = if shift_payload { VLE_LEN } else { needed_bytes }; -// to_write +// if shift_payload { +// VLE_LEN +// } else { +// needed_bytes +// } // })?; + // Ok(()) // } // } -// impl<'a, R> RCodec<&mut R, u64> for &Zenoh070 + +// impl WCodec<&u64, &mut W> for Zenoh080 // where -// R: Reader<'a>, +// W: Writer, // { -// type Error = (); +// type Output = Result<(), DidntWrite>; + +// fn write(self, writer: &mut W, x: &u64) -> Self::Output { +// self.write(writer, *x) +// } +// } + +// impl RCodec for Zenoh080 +// where +// R: Reader, +// { +// type Error = DidntRead; + // fn read(self, reader: &mut R) -> Result { // let mut buffer = [0; 8]; -// buffer[0] = match reader.read_u8() { -// None => return Err(()), -// Some(x) => x, -// }; +// buffer[0] = reader.read_u8()?; + // // GCC: `__builtin_ctz(~buffer[0])`, clang: `__tzcnt_u64(~buffer[0])` // let byte_count = (buffer[0].trailing_ones()) as usize; // if byte_count == 0 { // return Ok(u64::from_le_bytes(buffer) >> 1); // } + // let shift_payload = (byte_count == 8) as usize; // branches are evil -// let shift_payload_multiplier = (1 - shift_payload) as usize; +// let shift_payload_multiplier = 1 - shift_payload; // let len = byte_count + shift_payload_multiplier; -// reader.read(&mut buffer[shift_payload_multiplier..len]); +// reader.read_exact(&mut buffer[shift_payload_multiplier..len])?; // // the shift also removes the mask // Ok(u64::from_le_bytes(buffer) >> ((byte_count + 1) * shift_payload_multiplier) as u32) // } // } +// // usize +// impl WCodec for Zenoh080 +// where +// W: Writer, +// { +// type Output = Result<(), DidntWrite>; + +// fn write(self, writer: &mut W, x: usize) -> Self::Output { +// let x: u64 = x.try_into().map_err(|_| DidntWrite)?; +// self.write(writer, x) +// } +// } + +// impl RCodec for Zenoh080 +// where +// R: Reader, +// { +// type Error = DidntRead; + +// fn read(self, reader: &mut R) -> Result { +// let x: u64 = >::read(self, reader)?; +// x.try_into().map_err(|_| DidntRead) +// } +// } + // #[cfg(test)] // mod test { // #[test] -// fn zint_fuzz() { -// use crate::codec::*; +// fn u64_fuzz() { +// use crate::*; // use rand::Rng; -// let codec = crate::zenoh_070::Zenoh070::default(); +// use zenoh_buffers::{reader::HasReader, writer::HasWriter}; + +// const NUM: usize = 1_000; +// const LIMIT: [u64; 4] = [u8::MAX as u64, u16::MAX as u64, u32::MAX as u64, u64::MAX]; + +// let codec = Zenoh080::new(); // let mut rng = rand::thread_rng(); -// let dist = rand::distributions::Uniform::new(0, u8::MAX as u64); -// let mut buffer = Vec::with_capacity(9); -// for _ in 0..1000000 { -// buffer.clear(); -// let mut x: u64 = 1; -// for _ in 0..(rng.sample(&dist) % 8) { -// x *= rng.sample(&dist); -// } -// (&codec).write(&mut buffer, x).unwrap(); -// let mut reader = buffer.as_slice(); -// let y: u64 = (&codec).read(&mut reader).unwrap(); -// assert!(reader.is_empty()); -// assert_eq!( -// x, -// y, -// "\n {} ({}) != \n {} ({})\n{} ({})", -// binstr(&x.to_le_bytes()), -// x, -// binstr(&y.to_le_bytes()), -// y, -// binstr(&buffer), -// y, -// ); -// } -// buffer.clear(); -// let mut expected = Vec::new(); -// for _ in 0..1000 { -// let mut x: u64 = 1; -// for _ in 0..(rng.sample(&dist) % 8) { -// x *= rng.sample(&dist); +// for l in LIMIT.iter() { +// let mut values = Vec::with_capacity(NUM); +// let mut buffer = vec![]; + +// let mut writer = buffer.writer(); + +// for _ in 0..NUM { +// let x: u64 = rng.gen_range(0..=*l); +// values.push(x); +// codec.write(&mut writer, x).unwrap(); // } -// expected.push(x); -// let _ = (&codec).write(&mut buffer, x); -// } -// println!("{:x?}", expected); -// let mut reader = buffer.as_slice(); -// for (i, &x) in expected.iter().enumerate() { -// let y: u64 = (&codec).read(&mut reader).unwrap(); -// assert_eq!( -// x, -// y, -// "after reading {} numbers\n {} ({}) != \n {} ({})\n{} ({})", -// i, -// binstr(&x.to_le_bytes()), -// x, -// binstr(&y.to_le_bytes()), -// y, -// binstr(&buffer), -// y, -// ); -// } -// assert!(reader.is_empty()); -// } -// pub fn binstr(buffer: &[u8]) -> String { -// let mut result = String::with_capacity(9 * buffer.len()); -// for byte in buffer { -// for i in 0..8 { -// result.push(if (byte >> i) & 1 == 1 { '1' } else { '0' }) +// let mut reader = buffer.reader(); + +// for x in values.drain(..).take(NUM) { +// let y: u64 = codec.read(&mut reader).unwrap(); +// println!("{x} {y}"); +// assert_eq!(x, y); // } -// result.push(' ') + +// assert!(reader.is_empty()); // } -// result // } // } diff --git a/commons/zenoh-codec/src/core/zslice.rs b/commons/zenoh-codec/src/core/zslice.rs index 2f9cf0797a..cea0961b51 100644 --- a/commons/zenoh-codec/src/core/zslice.rs +++ b/commons/zenoh-codec/src/core/zslice.rs @@ -11,41 +11,72 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::{RCodec, WCodec, Zenoh060}; +use crate::{RCodec, WCodec, Zenoh080, Zenoh080Bounded}; use zenoh_buffers::{ reader::{DidntRead, Reader}, writer::{DidntWrite, Writer}, ZSlice, }; -#[cfg(feature = "shared-memory")] -pub(crate) mod kind { - pub(crate) const RAW: u8 = 0; - pub(crate) const SHM_INFO: u8 = 1; +// ZSlice - Bounded +macro_rules! zslice_impl { + ($bound:ty) => { + impl WCodec<&ZSlice, &mut W> for Zenoh080Bounded<$bound> + where + W: Writer, + { + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &ZSlice) -> Self::Output { + self.write(&mut *writer, x.len())?; + writer.write_zslice(x)?; + Ok(()) + } + } + + impl RCodec for Zenoh080Bounded<$bound> + where + R: Reader, + { + type Error = DidntRead; + + #[allow(clippy::uninit_vec)] + fn read(self, reader: &mut R) -> Result { + let len: usize = self.read(&mut *reader)?; + let zslice = reader.read_zslice(len)?; + Ok(zslice) + } + } + }; } -impl WCodec<&ZSlice, &mut W> for Zenoh060 +zslice_impl!(u8); +zslice_impl!(u16); +zslice_impl!(u32); +zslice_impl!(u64); +zslice_impl!(usize); + +// ZSlice +impl WCodec<&ZSlice, &mut W> for Zenoh080 where W: Writer, { type Output = Result<(), DidntWrite>; fn write(self, writer: &mut W, x: &ZSlice) -> Self::Output { - self.write(&mut *writer, x.len())?; - writer.write_zslice(x)?; - Ok(()) + let zodec = Zenoh080Bounded::::new(); + zodec.write(&mut *writer, x) } } -impl RCodec for Zenoh060 +impl RCodec for Zenoh080 where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - let len: usize = self.read(&mut *reader)?; - let zslice = reader.read_zslice(len)?; - Ok(zslice) + let zodec = Zenoh080Bounded::::new(); + zodec.read(&mut *reader) } } diff --git a/commons/zenoh-codec/src/lib.rs b/commons/zenoh-codec/src/lib.rs index 6538a52abf..14b41f6b0b 100644 --- a/commons/zenoh-codec/src/lib.rs +++ b/commons/zenoh-codec/src/lib.rs @@ -20,13 +20,15 @@ #![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; -mod common; -mod core; -mod scouting; -mod transport; -mod zenoh; +pub mod common; +pub mod core; +pub mod network; +pub mod scouting; +pub mod transport; +pub mod zenoh; -use zenoh_protocol::{core::Reliability, zenoh::ReplyContext}; +use ::core::marker::PhantomData; +use zenoh_protocol::core::Reliability; pub trait WCodec { type Output; @@ -38,53 +40,104 @@ pub trait RCodec { fn read(self, buffer: Buffer) -> Result; } -#[derive(Clone, Copy, Default)] -#[non_exhaustive] -pub struct Zenoh060; +// Calculate the length of the value once serialized +pub trait LCodec { + fn w_len(self, message: Message) -> usize; +} + +#[derive(Clone, Copy)] +pub struct Zenoh080; -#[derive(Clone, Copy, Default)] -#[non_exhaustive] -pub struct Zenoh060Header { +impl Zenoh080 { + pub const fn new() -> Self { + Self + } +} + +#[derive(Clone, Copy)] +pub struct Zenoh080Header { pub header: u8, - pub codec: Zenoh060, + pub codec: Zenoh080, +} + +impl Zenoh080Header { + pub const fn new(header: u8) -> Self { + Self { + header, + codec: Zenoh080, + } + } } -#[derive(Clone, Copy, Default)] -#[non_exhaustive] -pub struct Zenoh060Condition { +#[derive(Clone, Copy)] +pub struct Zenoh080Condition { pub condition: bool, - pub codec: Zenoh060, + pub codec: Zenoh080, } -impl Zenoh060Condition { +impl Zenoh080Condition { pub const fn new(condition: bool) -> Self { Self { condition, - codec: Zenoh060, + codec: Zenoh080, + } + } +} + +#[derive(Clone, Copy)] +pub struct Zenoh080Length { + pub length: usize, + pub codec: Zenoh080, +} + +impl Zenoh080Length { + pub const fn new(length: usize) -> Self { + Self { + length, + codec: Zenoh080, } } } -#[derive(Clone, Copy, Default)] -#[non_exhaustive] -pub struct Zenoh060Reliability { +#[derive(Clone, Copy)] +pub struct Zenoh080Reliability { pub reliability: Reliability, - pub codec: Zenoh060, + pub codec: Zenoh080, } -impl Zenoh060Reliability { +impl Zenoh080Reliability { pub const fn new(reliability: Reliability) -> Self { Self { reliability, - codec: Zenoh060, + codec: Zenoh080, } } } -#[derive(Clone, Default)] -#[non_exhaustive] -pub struct Zenoh060HeaderReplyContext { - pub header: u8, - pub reply_context: Option, - pub codec: Zenoh060, +#[derive(Clone, Copy)] +pub struct Zenoh080Bounded { + _t: PhantomData, +} + +impl Zenoh080Bounded { + pub const fn new() -> Self { + Self { _t: PhantomData } + } +} + +#[cfg(feature = "shared-memory")] +#[derive(Clone, Copy)] +pub struct Zenoh080Sliced { + is_sliced: bool, + codec: Zenoh080Bounded, +} + +#[cfg(feature = "shared-memory")] +impl Zenoh080Sliced { + pub const fn new(is_sliced: bool) -> Self { + Self { + is_sliced, + codec: Zenoh080Bounded::::new(), + } + } } diff --git a/commons/zenoh-codec/src/network/declare.rs b/commons/zenoh-codec/src/network/declare.rs new file mode 100644 index 0000000000..ae3a3dd77c --- /dev/null +++ b/commons/zenoh-codec/src/network/declare.rs @@ -0,0 +1,1088 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::{common::extension, RCodec, WCodec, Zenoh080, Zenoh080Condition, Zenoh080Header}; +use alloc::string::String; +use zenoh_buffers::{ + reader::{DidntRead, Reader}, + writer::{DidntWrite, HasWriter, Writer}, + ZBuf, +}; +use zenoh_protocol::{ + common::{iext, imsg, ZExtZ64}, + core::{ExprId, ExprLen, WireExpr}, + network::{ + declare::{ + self, common, interest, keyexpr, queryable, subscriber, token, Declare, DeclareBody, + }, + id, Mapping, + }, +}; + +// Declaration +impl WCodec<&DeclareBody, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &DeclareBody) -> Self::Output { + match x { + DeclareBody::DeclareKeyExpr(r) => self.write(&mut *writer, r)?, + DeclareBody::UndeclareKeyExpr(r) => self.write(&mut *writer, r)?, + DeclareBody::DeclareSubscriber(r) => self.write(&mut *writer, r)?, + DeclareBody::UndeclareSubscriber(r) => self.write(&mut *writer, r)?, + DeclareBody::DeclareQueryable(r) => self.write(&mut *writer, r)?, + DeclareBody::UndeclareQueryable(r) => self.write(&mut *writer, r)?, + DeclareBody::DeclareToken(r) => self.write(&mut *writer, r)?, + DeclareBody::UndeclareToken(r) => self.write(&mut *writer, r)?, + DeclareBody::DeclareInterest(r) => self.write(&mut *writer, r)?, + DeclareBody::FinalInterest(r) => self.write(&mut *writer, r)?, + DeclareBody::UndeclareInterest(r) => self.write(&mut *writer, r)?, + } + + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + + use declare::id::*; + let d = match imsg::mid(codec.header) { + D_KEYEXPR => DeclareBody::DeclareKeyExpr(codec.read(&mut *reader)?), + U_KEYEXPR => DeclareBody::UndeclareKeyExpr(codec.read(&mut *reader)?), + D_SUBSCRIBER => DeclareBody::DeclareSubscriber(codec.read(&mut *reader)?), + U_SUBSCRIBER => DeclareBody::UndeclareSubscriber(codec.read(&mut *reader)?), + D_QUERYABLE => DeclareBody::DeclareQueryable(codec.read(&mut *reader)?), + U_QUERYABLE => DeclareBody::UndeclareQueryable(codec.read(&mut *reader)?), + D_TOKEN => DeclareBody::DeclareToken(codec.read(&mut *reader)?), + U_TOKEN => DeclareBody::UndeclareToken(codec.read(&mut *reader)?), + D_INTEREST => DeclareBody::DeclareInterest(codec.read(&mut *reader)?), + F_INTEREST => DeclareBody::FinalInterest(codec.read(&mut *reader)?), + U_INTEREST => DeclareBody::UndeclareInterest(codec.read(&mut *reader)?), + _ => return Err(DidntRead), + }; + + Ok(d) + } +} + +// Declare +impl WCodec<&Declare, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &Declare) -> Self::Output { + // Header + let mut header = id::DECLARE; + let mut n_exts = ((x.ext_qos != declare::ext::QoSType::default()) as u8) + + (x.ext_tstamp.is_some() as u8) + + ((x.ext_nodeid != declare::ext::NodeIdType::default()) as u8); + if n_exts != 0 { + header |= declare::flag::Z; + } + self.write(&mut *writer, header)?; + + // Extensions + if x.ext_qos != declare::ext::QoSType::default() { + n_exts -= 1; + self.write(&mut *writer, (x.ext_qos, n_exts != 0))?; + } + if let Some(ts) = x.ext_tstamp.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (ts, n_exts != 0))?; + } + if x.ext_nodeid != declare::ext::NodeIdType::default() { + n_exts -= 1; + self.write(&mut *writer, (x.ext_nodeid, n_exts != 0))?; + } + + // Body + self.write(&mut *writer, &x.body)?; + + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + + codec.read(reader) + } +} + +impl RCodec for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + if imsg::mid(self.header) != id::DECLARE { + return Err(DidntRead); + } + + // Extensions + let mut ext_qos = declare::ext::QoSType::default(); + let mut ext_tstamp = None; + let mut ext_nodeid = declare::ext::NodeIdType::default(); + + let mut has_ext = imsg::has_flag(self.header, declare::flag::Z); + while has_ext { + let ext: u8 = self.codec.read(&mut *reader)?; + let eodec = Zenoh080Header::new(ext); + match iext::eid(ext) { + declare::ext::QoS::ID => { + let (q, ext): (declare::ext::QoSType, bool) = eodec.read(&mut *reader)?; + ext_qos = q; + has_ext = ext; + } + declare::ext::Timestamp::ID => { + let (t, ext): (declare::ext::TimestampType, bool) = eodec.read(&mut *reader)?; + ext_tstamp = Some(t); + has_ext = ext; + } + declare::ext::NodeId::ID => { + let (nid, ext): (declare::ext::NodeIdType, bool) = eodec.read(&mut *reader)?; + ext_nodeid = nid; + has_ext = ext; + } + _ => { + has_ext = extension::skip(reader, "Declare", ext)?; + } + } + } + + // Body + let body: DeclareBody = self.codec.read(&mut *reader)?; + + Ok(Declare { + body, + ext_qos, + ext_tstamp, + ext_nodeid, + }) + } +} + +// DeclareKeyExpr +impl WCodec<&keyexpr::DeclareKeyExpr, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &keyexpr::DeclareKeyExpr) -> Self::Output { + // Header + let mut header = declare::id::D_KEYEXPR; + if x.wire_expr.has_suffix() { + header |= keyexpr::flag::N; + } + self.write(&mut *writer, header)?; + + // Body + self.write(&mut *writer, x.id)?; + self.write(&mut *writer, &x.wire_expr)?; + + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + + codec.read(reader) + } +} + +impl RCodec for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + if imsg::mid(self.header) != declare::id::D_KEYEXPR { + return Err(DidntRead); + } + + let id: ExprId = self.codec.read(&mut *reader)?; + let ccond = Zenoh080Condition::new(imsg::has_flag(self.header, keyexpr::flag::N)); + let wire_expr: WireExpr<'static> = ccond.read(&mut *reader)?; + + // Extensions + let has_ext = imsg::has_flag(self.header, keyexpr::flag::Z); + if has_ext { + extension::skip_all(reader, "DeclareKeyExpr")?; + } + + Ok(keyexpr::DeclareKeyExpr { id, wire_expr }) + } +} + +// UndeclareKeyExpr +impl WCodec<&keyexpr::UndeclareKeyExpr, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &keyexpr::UndeclareKeyExpr) -> Self::Output { + // Header + let header = declare::id::U_KEYEXPR; + self.write(&mut *writer, header)?; + + // Body + self.write(&mut *writer, x.id)?; + + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + + codec.read(reader) + } +} + +impl RCodec for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + if imsg::mid(self.header) != declare::id::U_KEYEXPR { + return Err(DidntRead); + } + + let id: ExprId = self.codec.read(&mut *reader)?; + + // Extensions + let has_ext = imsg::has_flag(self.header, keyexpr::flag::Z); + if has_ext { + extension::skip_all(reader, "UndeclareKeyExpr")?; + } + + Ok(keyexpr::UndeclareKeyExpr { id }) + } +} + +// SubscriberInfo +crate::impl_zextz64!(subscriber::ext::SubscriberInfo, subscriber::ext::Info::ID); + +// DeclareSubscriber +impl WCodec<&subscriber::DeclareSubscriber, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &subscriber::DeclareSubscriber) -> Self::Output { + // Header + let mut header = declare::id::D_SUBSCRIBER; + let mut n_exts = (x.ext_info != subscriber::ext::SubscriberInfo::default()) as u8; + if n_exts != 0 { + header |= subscriber::flag::Z; + } + if x.wire_expr.mapping != Mapping::default() { + header |= subscriber::flag::M; + } + if x.wire_expr.has_suffix() { + header |= subscriber::flag::N; + } + self.write(&mut *writer, header)?; + + // Body + self.write(&mut *writer, x.id)?; + self.write(&mut *writer, &x.wire_expr)?; + + // Extensions + if x.ext_info != subscriber::ext::SubscriberInfo::default() { + n_exts -= 1; + self.write(&mut *writer, (x.ext_info, n_exts != 0))?; + } + + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + + codec.read(reader) + } +} + +impl RCodec for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + if imsg::mid(self.header) != declare::id::D_SUBSCRIBER { + return Err(DidntRead); + } + + // Body + let id: subscriber::SubscriberId = self.codec.read(&mut *reader)?; + let ccond = Zenoh080Condition::new(imsg::has_flag(self.header, subscriber::flag::N)); + let mut wire_expr: WireExpr<'static> = ccond.read(&mut *reader)?; + wire_expr.mapping = if imsg::has_flag(self.header, subscriber::flag::M) { + Mapping::Sender + } else { + Mapping::Receiver + }; + + // Extensions + let mut ext_info = subscriber::ext::SubscriberInfo::default(); + + let mut has_ext = imsg::has_flag(self.header, subscriber::flag::Z); + while has_ext { + let ext: u8 = self.codec.read(&mut *reader)?; + let eodec = Zenoh080Header::new(ext); + match iext::eid(ext) { + subscriber::ext::Info::ID => { + let (i, ext): (subscriber::ext::SubscriberInfo, bool) = + eodec.read(&mut *reader)?; + ext_info = i; + has_ext = ext; + } + _ => { + has_ext = extension::skip(reader, "DeclareSubscriber", ext)?; + } + } + } + + Ok(subscriber::DeclareSubscriber { + id, + wire_expr, + ext_info, + }) + } +} + +// UndeclareSubscriber +impl WCodec<&subscriber::UndeclareSubscriber, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &subscriber::UndeclareSubscriber) -> Self::Output { + // Header + let header = declare::id::U_SUBSCRIBER | subscriber::flag::Z; + self.write(&mut *writer, header)?; + + // Body + self.write(&mut *writer, x.id)?; + + // Extension + self.write(&mut *writer, (&x.ext_wire_expr, false))?; + + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + + codec.read(reader) + } +} + +impl RCodec for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + if imsg::mid(self.header) != declare::id::U_SUBSCRIBER { + return Err(DidntRead); + } + + // Body + let id: subscriber::SubscriberId = self.codec.read(&mut *reader)?; + + // Extensions + // WARNING: this is a temporary and mandatory extension used for undeclarations + let mut ext_wire_expr = common::ext::WireExprType::null(); + + let mut has_ext = imsg::has_flag(self.header, subscriber::flag::Z); + while has_ext { + let ext: u8 = self.codec.read(&mut *reader)?; + let eodec = Zenoh080Header::new(ext); + match iext::eid(ext) { + common::ext::WireExprExt::ID => { + let (we, ext): (common::ext::WireExprType, bool) = eodec.read(&mut *reader)?; + ext_wire_expr = we; + has_ext = ext; + } + _ => { + has_ext = extension::skip(reader, "UndeclareSubscriber", ext)?; + } + } + } + + Ok(subscriber::UndeclareSubscriber { id, ext_wire_expr }) + } +} + +// QueryableInfo +crate::impl_zextz64!(queryable::ext::QueryableInfo, queryable::ext::Info::ID); + +// DeclareQueryable +impl WCodec<&queryable::DeclareQueryable, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &queryable::DeclareQueryable) -> Self::Output { + // Header + let mut header = declare::id::D_QUERYABLE; + let mut n_exts = (x.ext_info != queryable::ext::QueryableInfo::default()) as u8; + if n_exts != 0 { + header |= subscriber::flag::Z; + } + if x.wire_expr.mapping != Mapping::default() { + header |= subscriber::flag::M; + } + if x.wire_expr.has_suffix() { + header |= subscriber::flag::N; + } + self.write(&mut *writer, header)?; + + // Body + self.write(&mut *writer, x.id)?; + self.write(&mut *writer, &x.wire_expr)?; + if x.ext_info != queryable::ext::QueryableInfo::default() { + n_exts -= 1; + self.write(&mut *writer, (x.ext_info, n_exts != 0))?; + } + + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + + codec.read(reader) + } +} + +impl RCodec for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + if imsg::mid(self.header) != declare::id::D_QUERYABLE { + return Err(DidntRead); + } + + // Body + let id: queryable::QueryableId = self.codec.read(&mut *reader)?; + let ccond = Zenoh080Condition::new(imsg::has_flag(self.header, queryable::flag::N)); + let mut wire_expr: WireExpr<'static> = ccond.read(&mut *reader)?; + wire_expr.mapping = if imsg::has_flag(self.header, queryable::flag::M) { + Mapping::Sender + } else { + Mapping::Receiver + }; + + // Extensions + let mut ext_info = queryable::ext::QueryableInfo::default(); + + let mut has_ext = imsg::has_flag(self.header, queryable::flag::Z); + while has_ext { + let ext: u8 = self.codec.read(&mut *reader)?; + let eodec = Zenoh080Header::new(ext); + match iext::eid(ext) { + queryable::ext::Info::ID => { + let (i, ext): (queryable::ext::QueryableInfo, bool) = + eodec.read(&mut *reader)?; + ext_info = i; + has_ext = ext; + } + _ => { + has_ext = extension::skip(reader, "DeclareQueryable", ext)?; + } + } + } + + Ok(queryable::DeclareQueryable { + id, + wire_expr, + ext_info, + }) + } +} + +// UndeclareQueryable +impl WCodec<&queryable::UndeclareQueryable, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &queryable::UndeclareQueryable) -> Self::Output { + // Header + let header = declare::id::U_QUERYABLE | queryable::flag::Z; + self.write(&mut *writer, header)?; + + // Body + self.write(&mut *writer, x.id)?; + + // Extension + self.write(&mut *writer, (&x.ext_wire_expr, false))?; + + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + + codec.read(reader) + } +} + +impl RCodec for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + if imsg::mid(self.header) != declare::id::U_QUERYABLE { + return Err(DidntRead); + } + + // Body + let id: queryable::QueryableId = self.codec.read(&mut *reader)?; + + // Extensions + // WARNING: this is a temporary and mandatory extension used for undeclarations + let mut ext_wire_expr = common::ext::WireExprType::null(); + + let mut has_ext = imsg::has_flag(self.header, queryable::flag::Z); + while has_ext { + let ext: u8 = self.codec.read(&mut *reader)?; + let eodec = Zenoh080Header::new(ext); + match iext::eid(ext) { + common::ext::WireExprExt::ID => { + let (we, ext): (common::ext::WireExprType, bool) = eodec.read(&mut *reader)?; + ext_wire_expr = we; + has_ext = ext; + } + _ => { + has_ext = extension::skip(reader, "UndeclareQueryable", ext)?; + } + } + } + + Ok(queryable::UndeclareQueryable { id, ext_wire_expr }) + } +} + +// DeclareToken +impl WCodec<&token::DeclareToken, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &token::DeclareToken) -> Self::Output { + // Header + let mut header = declare::id::D_TOKEN; + if x.wire_expr.mapping != Mapping::default() { + header |= subscriber::flag::M; + } + if x.wire_expr.has_suffix() { + header |= subscriber::flag::N; + } + self.write(&mut *writer, header)?; + + // Body + self.write(&mut *writer, x.id)?; + self.write(&mut *writer, &x.wire_expr)?; + + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(reader) + } +} + +impl RCodec for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + if imsg::mid(self.header) != declare::id::D_TOKEN { + return Err(DidntRead); + } + + // Body + let id: token::TokenId = self.codec.read(&mut *reader)?; + let ccond = Zenoh080Condition::new(imsg::has_flag(self.header, token::flag::N)); + let mut wire_expr: WireExpr<'static> = ccond.read(&mut *reader)?; + wire_expr.mapping = if imsg::has_flag(self.header, token::flag::M) { + Mapping::Sender + } else { + Mapping::Receiver + }; + + // Extensions + let has_ext = imsg::has_flag(self.header, token::flag::Z); + if has_ext { + extension::skip_all(reader, "DeclareToken")?; + } + + Ok(token::DeclareToken { id, wire_expr }) + } +} + +// UndeclareToken +impl WCodec<&token::UndeclareToken, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &token::UndeclareToken) -> Self::Output { + // Header + let header = declare::id::U_TOKEN | token::flag::Z; + self.write(&mut *writer, header)?; + + // Body + self.write(&mut *writer, x.id)?; + + // Extension + self.write(&mut *writer, (&x.ext_wire_expr, false))?; + + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + + codec.read(reader) + } +} + +impl RCodec for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + if imsg::mid(self.header) != declare::id::U_TOKEN { + return Err(DidntRead); + } + + // Body + let id: token::TokenId = self.codec.read(&mut *reader)?; + + // Extensions + // WARNING: this is a temporary and mandatory extension used for undeclarations + let mut ext_wire_expr = common::ext::WireExprType::null(); + + let mut has_ext = imsg::has_flag(self.header, interest::flag::Z); + while has_ext { + let ext: u8 = self.codec.read(&mut *reader)?; + let eodec = Zenoh080Header::new(ext); + match iext::eid(ext) { + common::ext::WireExprExt::ID => { + let (we, ext): (common::ext::WireExprType, bool) = eodec.read(&mut *reader)?; + ext_wire_expr = we; + has_ext = ext; + } + _ => { + has_ext = extension::skip(reader, "UndeclareToken", ext)?; + } + } + } + + Ok(token::UndeclareToken { id, ext_wire_expr }) + } +} + +// DeclareInterest +impl WCodec<&interest::DeclareInterest, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &interest::DeclareInterest) -> Self::Output { + // Header + let mut header = declare::id::D_INTEREST; + if x.wire_expr.mapping != Mapping::default() { + header |= subscriber::flag::M; + } + if x.wire_expr.has_suffix() { + header |= subscriber::flag::N; + } + self.write(&mut *writer, header)?; + + // Body + self.write(&mut *writer, x.id)?; + self.write(&mut *writer, &x.wire_expr)?; + self.write(&mut *writer, x.interest.as_u8())?; + + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(reader) + } +} + +impl RCodec for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + if imsg::mid(self.header) != declare::id::D_INTEREST { + return Err(DidntRead); + } + + // Body + let id: interest::InterestId = self.codec.read(&mut *reader)?; + let ccond = Zenoh080Condition::new(imsg::has_flag(self.header, token::flag::N)); + let mut wire_expr: WireExpr<'static> = ccond.read(&mut *reader)?; + wire_expr.mapping = if imsg::has_flag(self.header, token::flag::M) { + Mapping::Sender + } else { + Mapping::Receiver + }; + let interest: u8 = self.codec.read(&mut *reader)?; + + // Extensions + let has_ext = imsg::has_flag(self.header, token::flag::Z); + if has_ext { + extension::skip_all(reader, "DeclareInterest")?; + } + + Ok(interest::DeclareInterest { + id, + wire_expr, + interest: interest.into(), + }) + } +} + +// FinalInterest +impl WCodec<&interest::FinalInterest, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &interest::FinalInterest) -> Self::Output { + // Header + let header = declare::id::F_INTEREST; + self.write(&mut *writer, header)?; + + // Body + self.write(&mut *writer, x.id)?; + + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + + codec.read(reader) + } +} + +impl RCodec for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + if imsg::mid(self.header) != declare::id::F_INTEREST { + return Err(DidntRead); + } + + // Body + let id: interest::InterestId = self.codec.read(&mut *reader)?; + + // Extensions + let has_ext = imsg::has_flag(self.header, token::flag::Z); + if has_ext { + extension::skip_all(reader, "FinalInterest")?; + } + + Ok(interest::FinalInterest { id }) + } +} + +// UndeclareInterest +impl WCodec<&interest::UndeclareInterest, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &interest::UndeclareInterest) -> Self::Output { + // Header + let header = declare::id::U_INTEREST | interest::flag::Z; + self.write(&mut *writer, header)?; + + // Body + self.write(&mut *writer, x.id)?; + + // Extension + self.write(&mut *writer, (&x.ext_wire_expr, false))?; + + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + + codec.read(reader) + } +} + +impl RCodec for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + if imsg::mid(self.header) != declare::id::U_INTEREST { + return Err(DidntRead); + } + + // Body + let id: interest::InterestId = self.codec.read(&mut *reader)?; + + // Extensions + // WARNING: this is a temporary and mandatory extension used for undeclarations + let mut ext_wire_expr = common::ext::WireExprType::null(); + + let mut has_ext = imsg::has_flag(self.header, interest::flag::Z); + while has_ext { + let ext: u8 = self.codec.read(&mut *reader)?; + let eodec = Zenoh080Header::new(ext); + match iext::eid(ext) { + common::ext::WireExprExt::ID => { + let (we, ext): (common::ext::WireExprType, bool) = eodec.read(&mut *reader)?; + ext_wire_expr = we; + has_ext = ext; + } + _ => { + has_ext = extension::skip(reader, "UndeclareInterest", ext)?; + } + } + } + + Ok(interest::UndeclareInterest { id, ext_wire_expr }) + } +} + +// WARNING: this is a temporary extension used for undeclarations +impl WCodec<(&common::ext::WireExprType, bool), &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: (&common::ext::WireExprType, bool)) -> Self::Output { + let (x, more) = x; + + let codec = Zenoh080::new(); + let mut value = ZBuf::empty(); + let mut zriter = value.writer(); + + let mut flags: u8 = 0; + if x.wire_expr.has_suffix() { + flags |= 1; + } + if let Mapping::Receiver = x.wire_expr.mapping { + flags |= 1 << 1; + } + codec.write(&mut zriter, flags)?; + + codec.write(&mut zriter, x.wire_expr.scope)?; + if x.wire_expr.has_suffix() { + zriter.write_exact(x.wire_expr.suffix.as_bytes())?; + } + + let ext = common::ext::WireExprExt { value }; + codec.write(&mut *writer, (&ext, more))?; + + Ok(()) + } +} + +impl RCodec<(common::ext::WireExprType, bool), &mut R> for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<(common::ext::WireExprType, bool), Self::Error> { + use zenoh_buffers::reader::HasReader; + + let (ext, more): (common::ext::WireExprExt, bool) = self.read(&mut *reader)?; + + let mut zeader = ext.value.reader(); + let flags: u8 = self.codec.read(&mut zeader)?; + + let scope: ExprLen = self.codec.read(&mut zeader)?; + let suffix = if imsg::has_flag(flags, 1) { + let mut buff = zenoh_buffers::vec::uninit(zeader.remaining()); + zeader.read_exact(&mut buff)?; + String::from_utf8(buff).map_err(|_| DidntRead)? + } else { + String::new() + }; + let mapping = if imsg::has_flag(flags, 1 << 1) { + Mapping::Receiver + } else { + Mapping::Sender + }; + + Ok(( + common::ext::WireExprType { + wire_expr: WireExpr { + scope, + suffix: suffix.into(), + mapping, + }, + }, + more, + )) + } +} diff --git a/commons/zenoh-codec/src/network/mod.rs b/commons/zenoh-codec/src/network/mod.rs new file mode 100644 index 0000000000..7263c3fe27 --- /dev/null +++ b/commons/zenoh-codec/src/network/mod.rs @@ -0,0 +1,266 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +mod declare; +mod oam; +mod push; +mod request; +mod response; + +use crate::{ + LCodec, RCodec, WCodec, Zenoh080, Zenoh080Header, Zenoh080Length, Zenoh080Reliability, +}; +use zenoh_buffers::{ + reader::{DidntRead, Reader}, + writer::{DidntWrite, Writer}, +}; +use zenoh_protocol::{ + common::{imsg, ZExtZ64, ZExtZBufHeader}, + core::{Reliability, ZenohId}, + network::*, +}; + +// NetworkMessage +impl WCodec<&NetworkMessage, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &NetworkMessage) -> Self::Output { + match &x.body { + NetworkBody::Push(b) => self.write(&mut *writer, b), + NetworkBody::Request(b) => self.write(&mut *writer, b), + NetworkBody::Response(b) => self.write(&mut *writer, b), + NetworkBody::ResponseFinal(b) => self.write(&mut *writer, b), + NetworkBody::Declare(b) => self.write(&mut *writer, b), + NetworkBody::OAM(b) => self.write(&mut *writer, b), + } + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let codec = Zenoh080Reliability::new(Reliability::default()); + codec.read(reader) + } +} + +impl RCodec for Zenoh080Reliability +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.codec.read(&mut *reader)?; + + let codec = Zenoh080Header::new(header); + codec.read(&mut *reader) + } +} + +impl RCodec for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let body = match imsg::mid(self.header) { + id::PUSH => NetworkBody::Push(self.read(&mut *reader)?), + id::REQUEST => NetworkBody::Request(self.read(&mut *reader)?), + id::RESPONSE => NetworkBody::Response(self.read(&mut *reader)?), + id::RESPONSE_FINAL => NetworkBody::ResponseFinal(self.read(&mut *reader)?), + id::DECLARE => NetworkBody::Declare(self.read(&mut *reader)?), + id::OAM => NetworkBody::OAM(self.read(&mut *reader)?), + _ => return Err(DidntRead), + }; + + Ok(body.into()) + } +} + +// Extensions: QoS +impl WCodec<(ext::QoSType<{ ID }>, bool), &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: (ext::QoSType<{ ID }>, bool)) -> Self::Output { + let (x, more) = x; + let ext: ZExtZ64<{ ID }> = x.into(); + self.write(&mut *writer, (&ext, more)) + } +} + +impl RCodec<(ext::QoSType<{ ID }>, bool), &mut R> for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<(ext::QoSType<{ ID }>, bool), Self::Error> { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(reader) + } +} + +impl RCodec<(ext::QoSType<{ ID }>, bool), &mut R> for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<(ext::QoSType<{ ID }>, bool), Self::Error> { + let (ext, more): (ZExtZ64<{ ID }>, bool) = self.read(&mut *reader)?; + Ok((ext.into(), more)) + } +} + +// Extensions: Timestamp +impl WCodec<(&ext::TimestampType<{ ID }>, bool), &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: (&ext::TimestampType<{ ID }>, bool)) -> Self::Output { + let (tstamp, more) = x; + let header: ZExtZBufHeader<{ ID }> = ZExtZBufHeader::new(self.w_len(&tstamp.timestamp)); + self.write(&mut *writer, (&header, more))?; + self.write(&mut *writer, &tstamp.timestamp) + } +} + +impl RCodec<(ext::TimestampType<{ ID }>, bool), &mut R> for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<(ext::TimestampType<{ ID }>, bool), Self::Error> { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(reader) + } +} + +impl RCodec<(ext::TimestampType<{ ID }>, bool), &mut R> for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<(ext::TimestampType<{ ID }>, bool), Self::Error> { + let (_, more): (ZExtZBufHeader<{ ID }>, bool) = self.read(&mut *reader)?; + let timestamp: uhlc::Timestamp = self.codec.read(&mut *reader)?; + Ok((ext::TimestampType { timestamp }, more)) + } +} + +// Extensions: NodeId +impl WCodec<(ext::NodeIdType<{ ID }>, bool), &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: (ext::NodeIdType<{ ID }>, bool)) -> Self::Output { + let (x, more) = x; + let ext: ZExtZ64<{ ID }> = x.into(); + self.write(&mut *writer, (&ext, more)) + } +} + +impl RCodec<(ext::NodeIdType<{ ID }>, bool), &mut R> for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<(ext::NodeIdType<{ ID }>, bool), Self::Error> { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(reader) + } +} + +impl RCodec<(ext::NodeIdType<{ ID }>, bool), &mut R> for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<(ext::NodeIdType<{ ID }>, bool), Self::Error> { + let (ext, more): (ZExtZ64<{ ID }>, bool) = self.read(&mut *reader)?; + Ok((ext.into(), more)) + } +} + +// Extension: EntityId +impl LCodec<&ext::EntityIdType<{ ID }>> for Zenoh080 { + fn w_len(self, x: &ext::EntityIdType<{ ID }>) -> usize { + 1 + self.w_len(&x.zid) + self.w_len(x.eid) + } +} + +impl WCodec<(&ext::EntityIdType<{ ID }>, bool), &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: (&ext::EntityIdType<{ ID }>, bool)) -> Self::Output { + let (x, more) = x; + let header: ZExtZBufHeader<{ ID }> = ZExtZBufHeader::new(self.w_len(x)); + self.write(&mut *writer, (&header, more))?; + + let flags: u8 = (x.zid.size() as u8 - 1) << 4; + self.write(&mut *writer, flags)?; + + let lodec = Zenoh080Length::new(x.zid.size()); + lodec.write(&mut *writer, &x.zid)?; + + self.write(&mut *writer, x.eid)?; + Ok(()) + } +} + +impl RCodec<(ext::EntityIdType<{ ID }>, bool), &mut R> for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<(ext::EntityIdType<{ ID }>, bool), Self::Error> { + let (_, more): (ZExtZBufHeader<{ ID }>, bool) = self.read(&mut *reader)?; + + let flags: u8 = self.codec.read(&mut *reader)?; + let length = 1 + ((flags >> 4) as usize); + + let lodec = Zenoh080Length::new(length); + let zid: ZenohId = lodec.read(&mut *reader)?; + + let eid: u32 = self.codec.read(&mut *reader)?; + + Ok((ext::EntityIdType { zid, eid }, more)) + } +} diff --git a/commons/zenoh-codec/src/network/oam.rs b/commons/zenoh-codec/src/network/oam.rs new file mode 100644 index 0000000000..0e59421ba8 --- /dev/null +++ b/commons/zenoh-codec/src/network/oam.rs @@ -0,0 +1,156 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::{common::extension, RCodec, WCodec, Zenoh080, Zenoh080Header}; +use zenoh_buffers::{ + reader::{DidntRead, Reader}, + writer::{DidntWrite, Writer}, + ZBuf, +}; +use zenoh_protocol::{ + common::{iext, imsg, ZExtBody}, + network::{ + id, + oam::{ext, flag, Oam, OamId}, + }, +}; + +impl WCodec<&Oam, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &Oam) -> Self::Output { + // Header + let mut header = id::OAM; + match &x.body { + ZExtBody::Unit => { + header |= iext::ENC_UNIT; + } + ZExtBody::Z64(_) => { + header |= iext::ENC_Z64; + } + ZExtBody::ZBuf(_) => { + header |= iext::ENC_ZBUF; + } + } + let mut n_exts = + ((x.ext_qos != ext::QoSType::default()) as u8) + (x.ext_tstamp.is_some() as u8); + if n_exts != 0 { + header |= flag::Z; + } + self.write(&mut *writer, header)?; + + // Body + self.write(&mut *writer, x.id)?; + + // Extensions + if x.ext_qos != ext::QoSType::default() { + n_exts -= 1; + self.write(&mut *writer, (x.ext_qos, n_exts != 0))?; + } + if let Some(ts) = x.ext_tstamp.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (ts, n_exts != 0))?; + } + + // Payload + match &x.body { + ZExtBody::Unit => {} + ZExtBody::Z64(u64) => { + self.write(&mut *writer, u64)?; + } + ZExtBody::ZBuf(zbuf) => { + self.write(&mut *writer, zbuf)?; + } + } + + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(reader) + } +} + +impl RCodec for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + if imsg::mid(self.header) != id::OAM { + return Err(DidntRead); + } + + // Body + let id: OamId = self.codec.read(&mut *reader)?; + + // Extensions + let mut ext_qos = ext::QoSType::default(); + let mut ext_tstamp = None; + + let mut has_ext = imsg::has_flag(self.header, flag::Z); + while has_ext { + let ext: u8 = self.codec.read(&mut *reader)?; + let eodec = Zenoh080Header::new(ext); + match iext::eid(ext) { + ext::QoS::ID => { + let (q, ext): (ext::QoSType, bool) = eodec.read(&mut *reader)?; + ext_qos = q; + has_ext = ext; + } + ext::Timestamp::ID => { + let (t, ext): (ext::TimestampType, bool) = eodec.read(&mut *reader)?; + ext_tstamp = Some(t); + has_ext = ext; + } + _ => { + has_ext = extension::skip(reader, "OAM", ext)?; + } + } + } + + // Payload + let body = match self.header & iext::ENC_MASK { + iext::ENC_UNIT => ZExtBody::Unit, + iext::ENC_Z64 => { + let u64: u64 = self.codec.read(&mut *reader)?; + ZExtBody::Z64(u64) + } + iext::ENC_ZBUF => { + let zbuf: ZBuf = self.codec.read(&mut *reader)?; + ZExtBody::ZBuf(zbuf) + } + _ => return Err(DidntRead), + }; + + Ok(Oam { + id, + body, + ext_qos, + ext_tstamp, + }) + } +} diff --git a/commons/zenoh-codec/src/network/push.rs b/commons/zenoh-codec/src/network/push.rs new file mode 100644 index 0000000000..f6d4ee7f0c --- /dev/null +++ b/commons/zenoh-codec/src/network/push.rs @@ -0,0 +1,152 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::{common::extension, RCodec, WCodec, Zenoh080, Zenoh080Condition, Zenoh080Header}; +use zenoh_buffers::{ + reader::{DidntRead, Reader}, + writer::{DidntWrite, Writer}, +}; +use zenoh_protocol::{ + common::{iext, imsg}, + core::WireExpr, + network::{ + id, + push::{ext, flag}, + Mapping, Push, + }, + zenoh::PushBody, +}; + +impl WCodec<&Push, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &Push) -> Self::Output { + // Header + let mut header = id::PUSH; + let mut n_exts = ((x.ext_qos != ext::QoSType::default()) as u8) + + (x.ext_tstamp.is_some() as u8) + + ((x.ext_nodeid != ext::NodeIdType::default()) as u8); + if n_exts != 0 { + header |= flag::Z; + } + if x.wire_expr.mapping != Mapping::default() { + header |= flag::M; + } + if x.wire_expr.has_suffix() { + header |= flag::N; + } + self.write(&mut *writer, header)?; + + // Body + self.write(&mut *writer, &x.wire_expr)?; + + // Extensions + if x.ext_qos != ext::QoSType::default() { + n_exts -= 1; + self.write(&mut *writer, (x.ext_qos, n_exts != 0))?; + } + if let Some(ts) = x.ext_tstamp.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (ts, n_exts != 0))?; + } + if x.ext_nodeid != ext::NodeIdType::default() { + n_exts -= 1; + self.write(&mut *writer, (x.ext_nodeid, n_exts != 0))?; + } + + // Payload + self.write(&mut *writer, &x.payload)?; + + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(reader) + } +} + +impl RCodec for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + if imsg::mid(self.header) != id::PUSH { + return Err(DidntRead); + } + + // Body + let ccond = Zenoh080Condition::new(imsg::has_flag(self.header, flag::N)); + let mut wire_expr: WireExpr<'static> = ccond.read(&mut *reader)?; + wire_expr.mapping = if imsg::has_flag(self.header, flag::M) { + Mapping::Sender + } else { + Mapping::Receiver + }; + + // Extensions + let mut ext_qos = ext::QoSType::default(); + let mut ext_tstamp = None; + let mut ext_nodeid = ext::NodeIdType::default(); + + let mut has_ext = imsg::has_flag(self.header, flag::Z); + while has_ext { + let ext: u8 = self.codec.read(&mut *reader)?; + let eodec = Zenoh080Header::new(ext); + match iext::eid(ext) { + ext::QoS::ID => { + let (q, ext): (ext::QoSType, bool) = eodec.read(&mut *reader)?; + ext_qos = q; + has_ext = ext; + } + ext::Timestamp::ID => { + let (t, ext): (ext::TimestampType, bool) = eodec.read(&mut *reader)?; + ext_tstamp = Some(t); + has_ext = ext; + } + ext::NodeId::ID => { + let (nid, ext): (ext::NodeIdType, bool) = eodec.read(&mut *reader)?; + ext_nodeid = nid; + has_ext = ext; + } + _ => { + has_ext = extension::skip(reader, "Push", ext)?; + } + } + } + + // Payload + let payload: PushBody = self.codec.read(&mut *reader)?; + + Ok(Push { + wire_expr, + payload, + ext_qos, + ext_tstamp, + ext_nodeid, + }) + } +} diff --git a/commons/zenoh-codec/src/network/request.rs b/commons/zenoh-codec/src/network/request.rs new file mode 100644 index 0000000000..088c9e79f8 --- /dev/null +++ b/commons/zenoh-codec/src/network/request.rs @@ -0,0 +1,238 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::{ + common::extension, RCodec, WCodec, Zenoh080, Zenoh080Bounded, Zenoh080Condition, Zenoh080Header, +}; +use zenoh_buffers::{ + reader::{DidntRead, Reader}, + writer::{DidntWrite, Writer}, +}; +use zenoh_protocol::{ + common::{iext, imsg}, + core::WireExpr, + network::{ + id, + request::{ext, flag}, + Mapping, Request, RequestId, + }, + zenoh::RequestBody, +}; + +// Target +impl WCodec<(&ext::TargetType, bool), &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: (&ext::TargetType, bool)) -> Self::Output { + let (rt, more) = x; + let v = match rt { + ext::TargetType::BestMatching => 0, + ext::TargetType::All => 1, + ext::TargetType::AllComplete => 2, + #[cfg(feature = "complete_n")] + ext::TargetType::Complete(n) => 3 + *n, + }; + let ext = ext::Target::new(v); + self.write(&mut *writer, (&ext, more)) + } +} + +impl RCodec<(ext::TargetType, bool), &mut R> for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<(ext::TargetType, bool), Self::Error> { + let (ext, more): (ext::Target, bool) = self.read(&mut *reader)?; + let rt = match ext.value { + 0 => ext::TargetType::BestMatching, + 1 => ext::TargetType::All, + 2 => ext::TargetType::AllComplete, + #[cfg(feature = "complete_n")] + n => ext::TargetType::Complete(n - 3), + #[cfg(not(feature = "complete_n"))] + _ => return Err(DidntRead), + }; + Ok((rt, more)) + } +} + +impl WCodec<&Request, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &Request) -> Self::Output { + // Header + let mut header = id::REQUEST; + let mut n_exts = ((x.ext_qos != ext::QoSType::default()) as u8) + + (x.ext_tstamp.is_some() as u8) + + ((x.ext_target != ext::TargetType::default()) as u8) + + (x.ext_budget.is_some() as u8) + + (x.ext_timeout.is_some() as u8) + + ((x.ext_nodeid != ext::NodeIdType::default()) as u8); + if n_exts != 0 { + header |= flag::Z; + } + if x.wire_expr.mapping != Mapping::default() { + header |= flag::M; + } + if x.wire_expr.has_suffix() { + header |= flag::N; + } + self.write(&mut *writer, header)?; + + // Body + self.write(&mut *writer, x.id)?; + self.write(&mut *writer, &x.wire_expr)?; + + // Extensions + if x.ext_qos != ext::QoSType::default() { + n_exts -= 1; + self.write(&mut *writer, (x.ext_qos, n_exts != 0))?; + } + if let Some(ts) = x.ext_tstamp.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (ts, n_exts != 0))?; + } + if x.ext_target != ext::TargetType::default() { + n_exts -= 1; + self.write(&mut *writer, (&x.ext_target, n_exts != 0))?; + } + if let Some(l) = x.ext_budget.as_ref() { + n_exts -= 1; + let e = ext::Budget::new(l.get() as u64); + self.write(&mut *writer, (&e, n_exts != 0))?; + } + if let Some(to) = x.ext_timeout.as_ref() { + n_exts -= 1; + let e = ext::Timeout::new(to.as_millis() as u64); + self.write(&mut *writer, (&e, n_exts != 0))?; + } + if x.ext_nodeid != ext::NodeIdType::default() { + n_exts -= 1; + self.write(&mut *writer, (x.ext_nodeid, n_exts != 0))?; + } + + // Payload + self.write(&mut *writer, &x.payload)?; + + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(reader) + } +} + +impl RCodec for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + if imsg::mid(self.header) != id::REQUEST { + return Err(DidntRead); + } + + // Body + let bodec = Zenoh080Bounded::::new(); + let id: RequestId = bodec.read(&mut *reader)?; + let ccond = Zenoh080Condition::new(imsg::has_flag(self.header, flag::N)); + let mut wire_expr: WireExpr<'static> = ccond.read(&mut *reader)?; + wire_expr.mapping = if imsg::has_flag(self.header, flag::M) { + Mapping::Sender + } else { + Mapping::Receiver + }; + + // Extensions + let mut ext_qos = ext::QoSType::default(); + let mut ext_tstamp = None; + let mut ext_nodeid = ext::NodeIdType::default(); + let mut ext_target = ext::TargetType::default(); + let mut ext_limit = None; + let mut ext_timeout = None; + + let mut has_ext = imsg::has_flag(self.header, flag::Z); + while has_ext { + let ext: u8 = self.codec.read(&mut *reader)?; + let eodec = Zenoh080Header::new(ext); + match iext::eid(ext) { + ext::QoS::ID => { + let (q, ext): (ext::QoSType, bool) = eodec.read(&mut *reader)?; + ext_qos = q; + has_ext = ext; + } + ext::Timestamp::ID => { + let (t, ext): (ext::TimestampType, bool) = eodec.read(&mut *reader)?; + ext_tstamp = Some(t); + has_ext = ext; + } + ext::NodeId::ID => { + let (nid, ext): (ext::NodeIdType, bool) = eodec.read(&mut *reader)?; + ext_nodeid = nid; + has_ext = ext; + } + ext::Target::ID => { + let (rt, ext): (ext::TargetType, bool) = eodec.read(&mut *reader)?; + ext_target = rt; + has_ext = ext; + } + ext::Budget::ID => { + let (l, ext): (ext::Budget, bool) = eodec.read(&mut *reader)?; + ext_limit = ext::BudgetType::new(l.value as u32); + has_ext = ext; + } + ext::Timeout::ID => { + let (to, ext): (ext::Timeout, bool) = eodec.read(&mut *reader)?; + ext_timeout = Some(ext::TimeoutType::from_millis(to.value)); + has_ext = ext; + } + _ => { + has_ext = extension::skip(reader, "Request", ext)?; + } + } + } + + // Payload + let payload: RequestBody = self.codec.read(&mut *reader)?; + + Ok(Request { + id, + wire_expr, + payload, + ext_qos, + ext_tstamp, + ext_nodeid, + ext_target, + ext_budget: ext_limit, + ext_timeout, + }) + } +} diff --git a/commons/zenoh-codec/src/network/response.rs b/commons/zenoh-codec/src/network/response.rs new file mode 100644 index 0000000000..59d97fefda --- /dev/null +++ b/commons/zenoh-codec/src/network/response.rs @@ -0,0 +1,254 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::{ + common::extension, RCodec, WCodec, Zenoh080, Zenoh080Bounded, Zenoh080Condition, Zenoh080Header, +}; +use zenoh_buffers::{ + reader::{DidntRead, Reader}, + writer::{DidntWrite, Writer}, +}; +use zenoh_protocol::{ + common::{iext, imsg}, + core::WireExpr, + network::{ + id, + response::{ext, flag}, + Mapping, RequestId, Response, ResponseFinal, + }, + zenoh::ResponseBody, +}; + +// Response +impl WCodec<&Response, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &Response) -> Self::Output { + // Header + let mut header = id::RESPONSE; + let mut n_exts = ((x.ext_qos != ext::QoSType::default()) as u8) + + (x.ext_tstamp.is_some() as u8) + + (x.ext_respid.is_some() as u8); + if n_exts != 0 { + header |= flag::Z; + } + if x.wire_expr.mapping != Mapping::default() { + header |= flag::M; + } + if x.wire_expr.has_suffix() { + header |= flag::N; + } + self.write(&mut *writer, header)?; + + // Body + self.write(&mut *writer, x.rid)?; + self.write(&mut *writer, &x.wire_expr)?; + + // Extensions + if x.ext_qos != ext::QoSType::default() { + n_exts -= 1; + self.write(&mut *writer, (x.ext_qos, n_exts != 0))?; + } + if let Some(ts) = x.ext_tstamp.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (ts, n_exts != 0))?; + } + if let Some(ri) = x.ext_respid.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (ri, n_exts != 0))?; + } + + // Payload + self.write(&mut *writer, &x.payload)?; + + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(reader) + } +} + +impl RCodec for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + if imsg::mid(self.header) != id::RESPONSE { + return Err(DidntRead); + } + + // Body + let bodec = Zenoh080Bounded::::new(); + let rid: RequestId = bodec.read(&mut *reader)?; + let ccond = Zenoh080Condition::new(imsg::has_flag(self.header, flag::N)); + let mut wire_expr: WireExpr<'static> = ccond.read(&mut *reader)?; + wire_expr.mapping = if imsg::has_flag(self.header, flag::M) { + Mapping::Sender + } else { + Mapping::Receiver + }; + + // Extensions + let mut ext_qos = ext::QoSType::default(); + let mut ext_tstamp = None; + let mut ext_respid = None; + + let mut has_ext = imsg::has_flag(self.header, flag::Z); + while has_ext { + let ext: u8 = self.codec.read(&mut *reader)?; + let eodec = Zenoh080Header::new(ext); + match iext::eid(ext) { + ext::QoS::ID => { + let (q, ext): (ext::QoSType, bool) = eodec.read(&mut *reader)?; + ext_qos = q; + has_ext = ext; + } + ext::Timestamp::ID => { + let (t, ext): (ext::TimestampType, bool) = eodec.read(&mut *reader)?; + ext_tstamp = Some(t); + has_ext = ext; + } + ext::ResponderId::ID => { + let (t, ext): (ext::ResponderIdType, bool) = eodec.read(&mut *reader)?; + ext_respid = Some(t); + has_ext = ext; + } + _ => { + has_ext = extension::skip(reader, "Response", ext)?; + } + } + } + + // Payload + let payload: ResponseBody = self.codec.read(&mut *reader)?; + + Ok(Response { + rid, + wire_expr, + payload, + ext_qos, + ext_tstamp, + ext_respid, + }) + } +} + +// ResponseFinal +impl WCodec<&ResponseFinal, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &ResponseFinal) -> Self::Output { + // Header + let mut header = id::RESPONSE_FINAL; + let mut n_exts = + ((x.ext_qos != ext::QoSType::default()) as u8) + (x.ext_tstamp.is_some() as u8); + if n_exts != 0 { + header |= flag::Z; + } + self.write(&mut *writer, header)?; + + // Body + self.write(&mut *writer, x.rid)?; + + // Extensions + if x.ext_qos != ext::QoSType::default() { + n_exts -= 1; + self.write(&mut *writer, (x.ext_qos, n_exts != 0))?; + } + if let Some(ts) = x.ext_tstamp.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (ts, n_exts != 0))?; + } + + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(reader) + } +} + +impl RCodec for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + if imsg::mid(self.header) != id::RESPONSE_FINAL { + return Err(DidntRead); + } + + // Body + let bodec = Zenoh080Bounded::::new(); + let rid: RequestId = bodec.read(&mut *reader)?; + + // Extensions + let mut ext_qos = ext::QoSType::default(); + let mut ext_tstamp = None; + + let mut has_ext = imsg::has_flag(self.header, flag::Z); + while has_ext { + let ext: u8 = self.codec.read(&mut *reader)?; + let eodec = Zenoh080Header::new(ext); + match iext::eid(ext) { + ext::QoS::ID => { + let (q, ext): (ext::QoSType, bool) = eodec.read(&mut *reader)?; + ext_qos = q; + has_ext = ext; + } + ext::Timestamp::ID => { + let (t, ext): (ext::TimestampType, bool) = eodec.read(&mut *reader)?; + ext_tstamp = Some(t); + has_ext = ext; + } + _ => { + has_ext = extension::skip(reader, "ResponseFinal", ext)?; + } + } + } + + Ok(ResponseFinal { + rid, + ext_qos, + ext_tstamp, + }) + } +} diff --git a/commons/zenoh-codec/src/scouting/hello.rs b/commons/zenoh-codec/src/scouting/hello.rs index 882d1e0e9f..1793676cde 100644 --- a/commons/zenoh-codec/src/scouting/hello.rs +++ b/commons/zenoh-codec/src/scouting/hello.rs @@ -11,20 +11,22 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::{RCodec, WCodec, Zenoh060, Zenoh060Header}; +use crate::{RCodec, WCodec, Zenoh080, Zenoh080Header, Zenoh080Length}; use alloc::{vec, vec::Vec}; use zenoh_buffers::{ reader::{DidntRead, Reader}, writer::{DidntWrite, Writer}, }; use zenoh_protocol::{ - common::imsg, - core::{Locator, WhatAmI, ZInt, ZenohId}, - scouting::Hello, - transport::tmsg, + common::{imsg, ZExtUnknown}, + core::{Locator, WhatAmI, ZenohId}, + scouting::{ + hello::{flag, Hello}, + id, + }, }; -impl WCodec<&Hello, &mut W> for Zenoh060 +impl WCodec<&Hello, &mut W> for Zenoh080 where W: Writer, { @@ -32,79 +34,89 @@ where fn write(self, writer: &mut W, x: &Hello) -> Self::Output { // Header - let mut header = tmsg::id::HELLO; - if x.zid.is_some() { - header |= tmsg::flag::I - } - if x.whatami != WhatAmI::Router { - header |= tmsg::flag::W; - } + let mut header = id::HELLO; if !x.locators.is_empty() { - header |= tmsg::flag::L; + header |= flag::L; } self.write(&mut *writer, header)?; // Body - if let Some(zid) = x.zid.as_ref() { - self.write(&mut *writer, zid)?; - } - if x.whatami != WhatAmI::Router { - let wai: ZInt = x.whatami.into(); - self.write(&mut *writer, wai)?; - } + self.write(&mut *writer, x.version)?; + + let mut flags: u8 = 0; + let whatami: u8 = match x.whatami { + WhatAmI::Router => 0b00, + WhatAmI::Peer => 0b01, + WhatAmI::Client => 0b10, + }; + flags |= whatami & 0b11; + flags |= ((x.zid.size() - 1) as u8) << 4; + self.write(&mut *writer, flags)?; + + let lodec = Zenoh080Length::new(x.zid.size()); + lodec.write(&mut *writer, &x.zid)?; + if !x.locators.is_empty() { self.write(&mut *writer, x.locators.as_slice())?; } + Ok(()) } } -impl RCodec for Zenoh060 +impl RCodec for Zenoh080 where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); codec.read(reader) } } -impl RCodec for Zenoh060Header +impl RCodec for Zenoh080Header where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - if imsg::mid(self.header) != imsg::id::HELLO { + if imsg::mid(self.header) != id::HELLO { return Err(DidntRead); } - let zid = if imsg::has_flag(self.header, tmsg::flag::I) { - let zid: ZenohId = self.codec.read(&mut *reader)?; - Some(zid) - } else { - None - }; - let whatami = if imsg::has_flag(self.header, tmsg::flag::W) { - let wai: ZInt = self.codec.read(&mut *reader)?; - WhatAmI::try_from(wai).ok_or(DidntRead)? - } else { - WhatAmI::Router + // Body + let version: u8 = self.codec.read(&mut *reader)?; + let flags: u8 = self.codec.read(&mut *reader)?; + let whatami = match flags & 0b11 { + 0b00 => WhatAmI::Router, + 0b01 => WhatAmI::Peer, + 0b10 => WhatAmI::Client, + _ => return Err(DidntRead), }; - let locators = if imsg::has_flag(self.header, tmsg::flag::L) { + let length = 1 + ((flags >> 4) as usize); + let lodec = Zenoh080Length::new(length); + let zid: ZenohId = lodec.read(&mut *reader)?; + + let locators = if imsg::has_flag(self.header, flag::L) { let locs: Vec = self.codec.read(&mut *reader)?; locs } else { vec![] }; + // Extensions + let mut has_extensions = imsg::has_flag(self.header, flag::Z); + while has_extensions { + let (_, more): (ZExtUnknown, bool) = self.codec.read(&mut *reader)?; + has_extensions = more; + } + Ok(Hello { + version, zid, whatami, locators, diff --git a/commons/zenoh-codec/src/scouting/mod.rs b/commons/zenoh-codec/src/scouting/mod.rs index bfa1b2011a..70f6fb8065 100644 --- a/commons/zenoh-codec/src/scouting/mod.rs +++ b/commons/zenoh-codec/src/scouting/mod.rs @@ -14,27 +14,23 @@ mod hello; mod scout; -use crate::{RCodec, WCodec, Zenoh060, Zenoh060Header}; +use crate::{RCodec, WCodec, Zenoh080, Zenoh080Header}; use zenoh_buffers::{ reader::{DidntRead, Reader}, writer::{DidntWrite, Writer}, }; use zenoh_protocol::{ - common::{imsg, Attachment}, - scouting::{ScoutingBody, ScoutingMessage}, + common::imsg, + scouting::{id, ScoutingBody, ScoutingMessage}, }; -impl WCodec<&ScoutingMessage, &mut W> for Zenoh060 +impl WCodec<&ScoutingMessage, &mut W> for Zenoh080 where W: Writer, { type Output = Result<(), DidntWrite>; fn write(self, writer: &mut W, x: &ScoutingMessage) -> Self::Output { - if let Some(a) = x.attachment.as_ref() { - self.write(&mut *writer, a)?; - } - match &x.body { ScoutingBody::Scout(s) => self.write(&mut *writer, s), ScoutingBody::Hello(h) => self.write(&mut *writer, h), @@ -42,30 +38,21 @@ where } } -impl RCodec for Zenoh060 +impl RCodec for Zenoh080 where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - let mut codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); - let attachment = if imsg::mid(codec.header) == imsg::id::ATTACHMENT { - let a: Attachment = codec.read(&mut *reader)?; - codec.header = self.read(&mut *reader)?; - Some(a) - } else { - None - }; let body = match imsg::mid(codec.header) { - imsg::id::SCOUT => ScoutingBody::Scout(codec.read(&mut *reader)?), - imsg::id::HELLO => ScoutingBody::Hello(codec.read(&mut *reader)?), + id::SCOUT => ScoutingBody::Scout(codec.read(&mut *reader)?), + id::HELLO => ScoutingBody::Hello(codec.read(&mut *reader)?), _ => return Err(DidntRead), }; - Ok(ScoutingMessage { body, attachment }) + Ok(body.into()) } } diff --git a/commons/zenoh-codec/src/scouting/scout.rs b/commons/zenoh-codec/src/scouting/scout.rs index afa2fc91ec..941c455866 100644 --- a/commons/zenoh-codec/src/scouting/scout.rs +++ b/commons/zenoh-codec/src/scouting/scout.rs @@ -11,19 +11,22 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::{RCodec, WCodec, Zenoh060, Zenoh060Header}; +use crate::{RCodec, WCodec, Zenoh080, Zenoh080Header, Zenoh080Length}; +use core::convert::TryFrom; use zenoh_buffers::{ reader::{DidntRead, Reader}, writer::{DidntWrite, Writer}, }; use zenoh_protocol::{ - common::imsg, - core::{whatami::WhatAmIMatcher, ZInt}, - scouting::Scout, - transport::tmsg, + common::{imsg, ZExtUnknown}, + core::{whatami::WhatAmIMatcher, ZenohId}, + scouting::{ + id, + scout::{flag, Scout}, + }, }; -impl WCodec<&Scout, &mut W> for Zenoh060 +impl WCodec<&Scout, &mut W> for Zenoh080 where W: Writer, { @@ -31,62 +34,74 @@ where fn write(self, writer: &mut W, x: &Scout) -> Self::Output { // Header - let mut header = tmsg::id::SCOUT; - if x.zid_request { - header |= tmsg::flag::I; - } - if x.what.is_some() { - header |= tmsg::flag::W; - } + let header = id::SCOUT; self.write(&mut *writer, header)?; // Body - match x.what { - Some(w) => { - let w: ZInt = w.into(); - self.write(&mut *writer, w) - } - None => Ok(()), + self.write(&mut *writer, x.version)?; + + let mut flags: u8 = 0; + let what: u8 = x.what.into(); + flags |= what & 0b111; + if let Some(zid) = x.zid.as_ref() { + flags |= (((zid.size() - 1) as u8) << 4) | flag::I; + }; + self.write(&mut *writer, flags)?; + + if let Some(zid) = x.zid.as_ref() { + let lodec = Zenoh080Length::new(zid.size()); + lodec.write(&mut *writer, zid)?; } + + Ok(()) } } -impl RCodec for Zenoh060 +impl RCodec for Zenoh080 where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(reader) } } -impl RCodec for Zenoh060Header +impl RCodec for Zenoh080Header where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - if imsg::mid(self.header) != imsg::id::SCOUT { + if imsg::mid(self.header) != id::SCOUT { return Err(DidntRead); } - let zid_request = imsg::has_flag(self.header, tmsg::flag::I); - let what = if imsg::has_flag(self.header, tmsg::flag::W) { - let wai: ZInt = self.codec.read(reader)?; - let wai: ZInt = wai | 0x80; // FIXME: This fixes a misalignment with Zenoh-Pico, but it is implemented as a workaround because: - // 1) the new protocol will fix it as intended - // 2) we want to avoid breaking older builds, while fixing the misalignment with Zenoh-Pico - WhatAmIMatcher::try_from(wai) + // Body + let version: u8 = self.codec.read(&mut *reader)?; + let flags: u8 = self.codec.read(&mut *reader)?; + let what = WhatAmIMatcher::try_from(flags & 0b111).map_err(|_| DidntRead)?; + let zid = if imsg::has_flag(flags, flag::I) { + let length = 1 + ((flags >> 4) as usize); + let lodec = Zenoh080Length::new(length); + let zid: ZenohId = lodec.read(&mut *reader)?; + Some(zid) } else { None }; - Ok(Scout { what, zid_request }) + + // Extensions + let mut has_extensions = imsg::has_flag(self.header, flag::Z); + while has_extensions { + let (_, more): (ZExtUnknown, bool) = self.codec.read(&mut *reader)?; + has_extensions = more; + } + + Ok(Scout { version, what, zid }) } } diff --git a/commons/zenoh-codec/src/transport/close.rs b/commons/zenoh-codec/src/transport/close.rs index e47e6fd3d1..86b54f8688 100644 --- a/commons/zenoh-codec/src/transport/close.rs +++ b/commons/zenoh-codec/src/transport/close.rs @@ -11,18 +11,20 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::{RCodec, WCodec, Zenoh060, Zenoh060Header}; +use crate::{common::extension, RCodec, WCodec, Zenoh080, Zenoh080Header}; use zenoh_buffers::{ reader::{DidntRead, Reader}, writer::{DidntWrite, Writer}, }; use zenoh_protocol::{ common::imsg, - core::ZenohId, - transport::{tmsg, Close}, + transport::{ + close::{flag, Close}, + id, + }, }; -impl WCodec<&Close, &mut W> for Zenoh060 +impl WCodec<&Close, &mut W> for Zenoh080 where W: Writer, { @@ -30,63 +32,54 @@ where fn write(self, writer: &mut W, x: &Close) -> Self::Output { // Header - let mut header = tmsg::id::CLOSE; - if x.zid.is_some() { - header |= tmsg::flag::I; - } - if x.link_only { - header |= tmsg::flag::K; + let mut header = id::CLOSE; + if x.session { + header |= flag::S; } self.write(&mut *writer, header)?; // Body - if let Some(p) = x.zid.as_ref() { - self.write(&mut *writer, p)?; - } self.write(&mut *writer, x.reason)?; + Ok(()) } } -impl RCodec for Zenoh060 +impl RCodec for Zenoh080 where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(reader) } } -impl RCodec for Zenoh060Header +impl RCodec for Zenoh080Header where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - if imsg::mid(self.header) != tmsg::id::CLOSE { + if imsg::mid(self.header) != id::CLOSE { return Err(DidntRead); } + let session = imsg::has_flag(self.header, flag::S); - let link_only = imsg::has_flag(self.header, tmsg::flag::K); - let zid = if imsg::has_flag(self.header, tmsg::flag::I) { - let zid: ZenohId = self.codec.read(&mut *reader)?; - Some(zid) - } else { - None - }; + // Body let reason: u8 = self.codec.read(&mut *reader)?; - Ok(Close { - zid, - reason, - link_only, - }) + // Extensions + let has_ext = imsg::has_flag(self.header, flag::Z); + if has_ext { + extension::skip_all(reader, "Close")?; + } + + Ok(Close { reason, session }) } } diff --git a/commons/zenoh-codec/src/transport/fragment.rs b/commons/zenoh-codec/src/transport/fragment.rs new file mode 100644 index 0000000000..7cc827d378 --- /dev/null +++ b/commons/zenoh-codec/src/transport/fragment.rs @@ -0,0 +1,175 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::{common::extension, RCodec, WCodec, Zenoh080, Zenoh080Header}; +use zenoh_buffers::{ + reader::{BacktrackableReader, DidntRead, Reader}, + writer::{DidntWrite, Writer}, +}; +use zenoh_protocol::{ + common::{iext, imsg}, + core::Reliability, + transport::{ + fragment::{ext, flag, Fragment, FragmentHeader, TransportSn}, + id, + }, +}; + +// FragmentHeader +impl WCodec<&FragmentHeader, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &FragmentHeader) -> Self::Output { + // Header + let mut header = id::FRAGMENT; + if let Reliability::Reliable = x.reliability { + header |= flag::R; + } + if x.more { + header |= flag::M; + } + if x.ext_qos != ext::QoSType::default() { + header |= flag::Z; + } + self.write(&mut *writer, header)?; + + // Body + self.write(&mut *writer, x.sn)?; + + // Extensions + if x.ext_qos != ext::QoSType::default() { + self.write(&mut *writer, (x.ext_qos, false))?; + } + + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(reader) + } +} + +impl RCodec for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + if imsg::mid(self.header) != id::FRAGMENT { + return Err(DidntRead); + } + + let reliability = match imsg::has_flag(self.header, flag::R) { + true => Reliability::Reliable, + false => Reliability::BestEffort, + }; + let more = imsg::has_flag(self.header, flag::M); + let sn: TransportSn = self.codec.read(&mut *reader)?; + + // Extensions + let mut ext_qos = ext::QoSType::default(); + + let mut has_ext = imsg::has_flag(self.header, flag::Z); + while has_ext { + let ext: u8 = self.codec.read(&mut *reader)?; + let eodec = Zenoh080Header::new(ext); + match iext::eid(ext) { + ext::QoS::ID => { + let (q, ext): (ext::QoSType, bool) = eodec.read(&mut *reader)?; + ext_qos = q; + has_ext = ext; + } + _ => { + has_ext = extension::skip(reader, "Fragment", ext)?; + } + } + } + + Ok(FragmentHeader { + reliability, + more, + sn, + ext_qos, + }) + } +} + +// Fragment +impl WCodec<&Fragment, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &Fragment) -> Self::Output { + // Header + let header = FragmentHeader { + reliability: x.reliability, + more: x.more, + sn: x.sn, + ext_qos: x.ext_qos, + }; + self.write(&mut *writer, &header)?; + + // Body + writer.write_zslice(&x.payload)?; + + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader + BacktrackableReader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(reader) + } +} + +impl RCodec for Zenoh080Header +where + R: Reader + BacktrackableReader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: FragmentHeader = self.read(&mut *reader)?; + let payload = reader.read_zslice(reader.remaining())?; + + Ok(Fragment { + reliability: header.reliability, + more: header.more, + sn: header.sn, + ext_qos: header.ext_qos, + payload, + }) + } +} diff --git a/commons/zenoh-codec/src/transport/frame.rs b/commons/zenoh-codec/src/transport/frame.rs index 91f9efb3e0..1293dc950c 100644 --- a/commons/zenoh-codec/src/transport/frame.rs +++ b/commons/zenoh-codec/src/transport/frame.rs @@ -11,116 +11,111 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::{RCodec, WCodec, Zenoh060, Zenoh060Header, Zenoh060Reliability}; +use crate::{common::extension, RCodec, WCodec, Zenoh080, Zenoh080Header, Zenoh080Reliability}; use alloc::vec::Vec; use zenoh_buffers::{ reader::{BacktrackableReader, DidntRead, Reader}, writer::{DidntWrite, Writer}, }; use zenoh_protocol::{ - common::imsg, - core::{Channel, Priority, Reliability, ZInt}, - transport::{tmsg, Frame, FrameHeader, FrameKind, FramePayload}, - zenoh::ZenohMessage, + common::{iext, imsg}, + core::Reliability, + network::NetworkMessage, + transport::{ + frame::{ext, flag, Frame, FrameHeader}, + id, TransportSn, + }, }; // FrameHeader -impl WCodec<&FrameHeader, &mut W> for Zenoh060 +impl WCodec<&FrameHeader, &mut W> for Zenoh080 where W: Writer, { type Output = Result<(), DidntWrite>; fn write(self, writer: &mut W, x: &FrameHeader) -> Self::Output { - // Decorator - if x.channel.priority != Priority::default() { - self.write(&mut *writer, &x.channel.priority)?; - } - // Header - let mut header = tmsg::id::FRAME; - if let Reliability::Reliable = x.channel.reliability { - header |= tmsg::flag::R; + let mut header = id::FRAME; + if let Reliability::Reliable = x.reliability { + header |= flag::R; } - match x.kind { - FrameKind::Messages => {} - FrameKind::SomeFragment => { - header |= tmsg::flag::F; - } - FrameKind::LastFragment => { - header |= tmsg::flag::F; - header |= tmsg::flag::E; - } + if x.ext_qos != ext::QoSType::default() { + header |= flag::Z; } self.write(&mut *writer, header)?; // Body self.write(&mut *writer, x.sn)?; + // Extensions + if x.ext_qos != ext::QoSType::default() { + self.write(&mut *writer, (x.ext_qos, false))?; + } + Ok(()) } } -impl RCodec for Zenoh060 +impl RCodec for Zenoh080 where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); codec.read(reader) } } -impl RCodec for Zenoh060Header +impl RCodec for Zenoh080Header where R: Reader, { type Error = DidntRead; - fn read(mut self, reader: &mut R) -> Result { - let mut priority = Priority::default(); - if imsg::mid(self.header) == tmsg::id::PRIORITY { - // Decode priority - priority = self.read(&mut *reader)?; - // Read next header - self.header = self.codec.read(&mut *reader)?; - } - - if imsg::mid(self.header) != tmsg::id::FRAME { + fn read(self, reader: &mut R) -> Result { + if imsg::mid(self.header) != id::FRAME { return Err(DidntRead); } - let reliability = match imsg::has_flag(self.header, tmsg::flag::R) { + let reliability = match imsg::has_flag(self.header, flag::R) { true => Reliability::Reliable, false => Reliability::BestEffort, }; - let channel = Channel { - priority, - reliability, - }; - let sn: ZInt = self.codec.read(&mut *reader)?; - - let kind = if imsg::has_flag(self.header, tmsg::flag::F) { - if imsg::has_flag(self.header, tmsg::flag::E) { - FrameKind::LastFragment - } else { - FrameKind::SomeFragment + let sn: TransportSn = self.codec.read(&mut *reader)?; + + // Extensions + let mut ext_qos = ext::QoSType::default(); + + let mut has_ext = imsg::has_flag(self.header, flag::Z); + while has_ext { + let ext: u8 = self.codec.read(&mut *reader)?; + let eodec = Zenoh080Header::new(ext); + match iext::eid(ext) { + ext::QoS::ID => { + let (q, ext): (ext::QoSType, bool) = eodec.read(&mut *reader)?; + ext_qos = q; + has_ext = ext; + } + _ => { + has_ext = extension::skip(reader, "Frame", ext)?; + } } - } else { - FrameKind::Messages - }; + } - Ok(FrameHeader { channel, sn, kind }) + Ok(FrameHeader { + reliability, + sn, + ext_qos, + }) } } // Frame -impl WCodec<&Frame, &mut W> for Zenoh060 +impl WCodec<&Frame, &mut W> for Zenoh080 where W: Writer, { @@ -128,52 +123,36 @@ where fn write(self, writer: &mut W, x: &Frame) -> Self::Output { // Header - let kind = match &x.payload { - FramePayload::Fragment { is_final, .. } => { - if *is_final { - FrameKind::LastFragment - } else { - FrameKind::SomeFragment - } - } - FramePayload::Messages { .. } => FrameKind::Messages, - }; let header = FrameHeader { - channel: x.channel, + reliability: x.reliability, sn: x.sn, - kind, + ext_qos: x.ext_qos, }; self.write(&mut *writer, &header)?; // Body - match &x.payload { - FramePayload::Fragment { buffer, .. } => writer.write_zslice(buffer)?, - FramePayload::Messages { messages } => { - for m in messages.iter() { - self.write(&mut *writer, m)?; - } - } + for m in x.payload.iter() { + self.write(&mut *writer, m)?; } + Ok(()) } } -impl RCodec for Zenoh060 +impl RCodec for Zenoh080 where R: Reader + BacktrackableReader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); codec.read(reader) } } -impl RCodec for Zenoh060Header +impl RCodec for Zenoh080Header where R: Reader + BacktrackableReader, { @@ -182,39 +161,24 @@ where fn read(self, reader: &mut R) -> Result { let header: FrameHeader = self.read(&mut *reader)?; - let payload = match header.kind { - FrameKind::Messages => { - let rcode = Zenoh060Reliability { - reliability: header.channel.reliability, - ..Default::default() - }; - - let mut messages: Vec = Vec::with_capacity(1); - while reader.can_read() { - let mark = reader.mark(); - let res: Result = rcode.read(&mut *reader); - match res { - Ok(m) => messages.push(m), - Err(_) => { - reader.rewind(mark); - break; - } - } + let rcode = Zenoh080Reliability::new(header.reliability); + let mut payload = Vec::new(); + while reader.can_read() { + let mark = reader.mark(); + let res: Result = rcode.read(&mut *reader); + match res { + Ok(m) => payload.push(m), + Err(_) => { + reader.rewind(mark); + break; } - FramePayload::Messages { messages } } - FrameKind::SomeFragment | FrameKind::LastFragment => { - // A fragmented frame is not supposed to be followed by - // any other frame in the same batch. Read all the bytes. - let buffer = reader.read_zslice(reader.remaining())?; - let is_final = header.kind == FrameKind::LastFragment; - FramePayload::Fragment { buffer, is_final } - } - }; + } Ok(Frame { - channel: header.channel, + reliability: header.reliability, sn: header.sn, + ext_qos: header.ext_qos, payload, }) } diff --git a/commons/zenoh-codec/src/transport/init.rs b/commons/zenoh-codec/src/transport/init.rs index 3e6883e7b6..2f5dae25c9 100644 --- a/commons/zenoh-codec/src/transport/init.rs +++ b/commons/zenoh-codec/src/transport/init.rs @@ -11,211 +11,349 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::{RCodec, WCodec, Zenoh060, Zenoh060Header}; +use crate::{ + common::extension, RCodec, WCodec, Zenoh080, Zenoh080Bounded, Zenoh080Header, Zenoh080Length, +}; use zenoh_buffers::{ reader::{DidntRead, Reader}, writer::{DidntWrite, Writer}, ZSlice, }; use zenoh_protocol::{ - common::imsg, - core::{WhatAmI, ZInt, ZenohId}, - defaults::SEQ_NUM_RES, - transport::{tmsg, InitAck, InitSyn}, + common::{iext, imsg}, + core::{Resolution, WhatAmI, ZenohId}, + transport::{ + batch_size, id, + init::{ext, flag, InitAck, InitSyn}, + BatchSize, + }, }; // InitSyn -impl WCodec<&InitSyn, &mut W> for Zenoh060 +impl WCodec<&InitSyn, &mut W> for Zenoh080 where W: Writer, { type Output = Result<(), DidntWrite>; fn write(self, writer: &mut W, x: &InitSyn) -> Self::Output { - fn has_options(x: &InitSyn) -> bool { - x.is_qos - } - - fn options(x: &InitSyn) -> ZInt { - let mut options = 0; - if x.is_qos { - options |= tmsg::init_options::QOS; - } - options - } - // Header - let mut header = tmsg::id::INIT; - if x.sn_resolution != SEQ_NUM_RES { - header |= tmsg::flag::S; + let mut header = id::INIT; + if x.resolution != Resolution::default() || x.batch_size != batch_size::UNICAST { + header |= flag::S; } - if has_options(x) { - header |= tmsg::flag::O; + let mut n_exts = (x.ext_qos.is_some() as u8) + + (x.ext_shm.is_some() as u8) + + (x.ext_auth.is_some() as u8) + + (x.ext_mlink.is_some() as u8); + if n_exts != 0 { + header |= flag::Z; } self.write(&mut *writer, header)?; // Body - if has_options(x) { - self.write(&mut *writer, options(x))?; - } self.write(&mut *writer, x.version)?; - let wai: ZInt = x.whatami.into(); - self.write(&mut *writer, wai)?; - self.write(&mut *writer, &x.zid)?; - if imsg::has_flag(header, tmsg::flag::S) { - self.write(&mut *writer, x.sn_resolution)?; + + let whatami: u8 = match x.whatami { + WhatAmI::Router => 0b00, + WhatAmI::Peer => 0b01, + WhatAmI::Client => 0b10, + }; + let flags: u8 = ((x.zid.size() as u8 - 1) << 4) | whatami; + self.write(&mut *writer, flags)?; + + let lodec = Zenoh080Length::new(x.zid.size()); + lodec.write(&mut *writer, &x.zid)?; + + if imsg::has_flag(header, flag::S) { + self.write(&mut *writer, x.resolution.as_u8())?; + self.write(&mut *writer, x.batch_size.to_le_bytes())?; + } + + // Extensions + if let Some(qos) = x.ext_qos.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (qos, n_exts != 0))?; + } + if let Some(shm) = x.ext_shm.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (shm, n_exts != 0))?; } + if let Some(auth) = x.ext_auth.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (auth, n_exts != 0))?; + } + if let Some(mlink) = x.ext_mlink.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (mlink, n_exts != 0))?; + } + Ok(()) } } -impl RCodec for Zenoh060 +impl RCodec for Zenoh080 where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); codec.read(reader) } } -impl RCodec for Zenoh060Header +impl RCodec for Zenoh080Header where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - if imsg::mid(self.header) != tmsg::id::INIT || imsg::has_flag(self.header, tmsg::flag::A) { + if imsg::mid(self.header) != id::INIT || imsg::has_flag(self.header, flag::A) { return Err(DidntRead); } - let options: ZInt = if imsg::has_flag(self.header, tmsg::flag::O) { - self.codec.read(&mut *reader)? - } else { - 0 - }; + // Body let version: u8 = self.codec.read(&mut *reader)?; - let wai: ZInt = self.codec.read(&mut *reader)?; - let whatami = WhatAmI::try_from(wai).ok_or(DidntRead)?; - let zid: ZenohId = self.codec.read(&mut *reader)?; - let sn_resolution: ZInt = if imsg::has_flag(self.header, tmsg::flag::S) { - self.codec.read(&mut *reader)? - } else { - SEQ_NUM_RES + + let flags: u8 = self.codec.read(&mut *reader)?; + let whatami = match flags & 0b11 { + 0b00 => WhatAmI::Router, + 0b01 => WhatAmI::Peer, + 0b10 => WhatAmI::Client, + _ => return Err(DidntRead), }; - let is_qos = imsg::has_option(options, tmsg::init_options::QOS); + let length = 1 + ((flags >> 4) as usize); + let lodec = Zenoh080Length::new(length); + let zid: ZenohId = lodec.read(&mut *reader)?; + + let mut resolution = Resolution::default(); + let mut batch_size = batch_size::UNICAST.to_le_bytes(); + if imsg::has_flag(self.header, flag::S) { + let flags: u8 = self.codec.read(&mut *reader)?; + resolution = Resolution::from(flags & 0b00111111); + batch_size = self.codec.read(&mut *reader)?; + } + let batch_size = BatchSize::from_le_bytes(batch_size); + + // Extensions + let mut ext_qos = None; + let mut ext_shm = None; + let mut ext_auth = None; + let mut ext_mlink = None; + + let mut has_ext = imsg::has_flag(self.header, flag::Z); + while has_ext { + let ext: u8 = self.codec.read(&mut *reader)?; + let eodec = Zenoh080Header::new(ext); + match iext::eid(ext) { + ext::QoS::ID => { + let (q, ext): (ext::QoS, bool) = eodec.read(&mut *reader)?; + ext_qos = Some(q); + has_ext = ext; + } + ext::Shm::ID => { + let (s, ext): (ext::Shm, bool) = eodec.read(&mut *reader)?; + ext_shm = Some(s); + has_ext = ext; + } + ext::Auth::ID => { + let (a, ext): (ext::Auth, bool) = eodec.read(&mut *reader)?; + ext_auth = Some(a); + has_ext = ext; + } + ext::MultiLink::ID => { + let (a, ext): (ext::MultiLink, bool) = eodec.read(&mut *reader)?; + ext_mlink = Some(a); + has_ext = ext; + } + _ => { + has_ext = extension::skip(reader, "InitSyn", ext)?; + } + } + } Ok(InitSyn { version, whatami, zid, - sn_resolution, - is_qos, + resolution, + batch_size, + ext_qos, + ext_shm, + ext_auth, + ext_mlink, }) } } // InitAck -impl WCodec<&InitAck, &mut W> for Zenoh060 +impl WCodec<&InitAck, &mut W> for Zenoh080 where W: Writer, { type Output = Result<(), DidntWrite>; fn write(self, writer: &mut W, x: &InitAck) -> Self::Output { - fn has_options(x: &InitAck) -> bool { - x.is_qos - } - - fn options(x: &InitAck) -> ZInt { - let mut options = 0; - if x.is_qos { - options |= tmsg::init_options::QOS; - } - options - } - // Header - let mut header = tmsg::id::INIT; - header |= tmsg::flag::A; - if x.sn_resolution.is_some() { - header |= tmsg::flag::S; + let mut header = id::INIT | flag::A; + if x.resolution != Resolution::default() || x.batch_size != batch_size::UNICAST { + header |= flag::S; } - if has_options(x) { - header |= tmsg::flag::O; + let mut n_exts = (x.ext_qos.is_some() as u8) + + (x.ext_shm.is_some() as u8) + + (x.ext_auth.is_some() as u8) + + (x.ext_mlink.is_some() as u8); + if n_exts != 0 { + header |= flag::Z; } self.write(&mut *writer, header)?; // Body - if has_options(x) { - self.write(&mut *writer, options(x))?; + self.write(&mut *writer, x.version)?; + + let whatami: u8 = match x.whatami { + WhatAmI::Router => 0b00, + WhatAmI::Peer => 0b01, + WhatAmI::Client => 0b10, + }; + let flags: u8 = ((x.zid.size() as u8 - 1) << 4) | whatami; + self.write(&mut *writer, flags)?; + + let lodec = Zenoh080Length::new(x.zid.size()); + lodec.write(&mut *writer, &x.zid)?; + + if imsg::has_flag(header, flag::S) { + self.write(&mut *writer, x.resolution.as_u8())?; + self.write(&mut *writer, x.batch_size.to_le_bytes())?; + } + + let zodec = Zenoh080Bounded::::new(); + zodec.write(&mut *writer, &x.cookie)?; + + // Extensions + if let Some(qos) = x.ext_qos.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (qos, n_exts != 0))?; + } + if let Some(shm) = x.ext_shm.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (shm, n_exts != 0))?; + } + if let Some(auth) = x.ext_auth.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (auth, n_exts != 0))?; } - let wai: ZInt = x.whatami.into(); - self.write(&mut *writer, wai)?; - self.write(&mut *writer, &x.zid)?; - if let Some(snr) = x.sn_resolution { - self.write(&mut *writer, snr)?; + if let Some(mlink) = x.ext_mlink.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (mlink, n_exts != 0))?; } - self.write(&mut *writer, &x.cookie)?; + Ok(()) } } -impl RCodec for Zenoh060 +impl RCodec for Zenoh080 where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); codec.read(reader) } } -impl RCodec for Zenoh060Header +impl RCodec for Zenoh080Header where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - if imsg::mid(self.header) != imsg::id::INIT || !imsg::has_flag(self.header, tmsg::flag::A) { + if imsg::mid(self.header) != id::INIT || !imsg::has_flag(self.header, flag::A) { return Err(DidntRead); } - let options: ZInt = if imsg::has_flag(self.header, tmsg::flag::O) { - self.codec.read(&mut *reader)? - } else { - 0 - }; - let wai: ZInt = self.codec.read(&mut *reader)?; - let whatami = WhatAmI::try_from(wai).ok_or(DidntRead)?; - let zid: ZenohId = self.codec.read(&mut *reader)?; - let sn_resolution = if imsg::has_flag(self.header, tmsg::flag::S) { - let snr: ZInt = self.codec.read(&mut *reader)?; - Some(snr) - } else { - None + // Body + let version: u8 = self.codec.read(&mut *reader)?; + + let flags: u8 = self.codec.read(&mut *reader)?; + let whatami = match flags & 0b11 { + 0b00 => WhatAmI::Router, + 0b01 => WhatAmI::Peer, + 0b10 => WhatAmI::Client, + _ => return Err(DidntRead), }; - let is_qos = imsg::has_option(options, tmsg::init_options::QOS); - let cookie: ZSlice = self.codec.read(&mut *reader)?; + let length = 1 + ((flags >> 4) as usize); + let lodec = Zenoh080Length::new(length); + let zid: ZenohId = lodec.read(&mut *reader)?; + + let mut resolution = Resolution::default(); + let mut batch_size = batch_size::UNICAST.to_le_bytes(); + if imsg::has_flag(self.header, flag::S) { + let flags: u8 = self.codec.read(&mut *reader)?; + resolution = Resolution::from(flags & 0b00111111); + batch_size = self.codec.read(&mut *reader)?; + } + let batch_size = BatchSize::from_le_bytes(batch_size); + + let zodec = Zenoh080Bounded::::new(); + let cookie: ZSlice = zodec.read(&mut *reader)?; + + // Extensions + let mut ext_qos = None; + let mut ext_shm = None; + let mut ext_auth = None; + let mut ext_mlink = None; + + let mut has_ext = imsg::has_flag(self.header, flag::Z); + while has_ext { + let ext: u8 = self.codec.read(&mut *reader)?; + let eodec = Zenoh080Header::new(ext); + match iext::eid(ext) { + ext::QoS::ID => { + let (q, ext): (ext::QoS, bool) = eodec.read(&mut *reader)?; + ext_qos = Some(q); + has_ext = ext; + } + ext::Shm::ID => { + let (s, ext): (ext::Shm, bool) = eodec.read(&mut *reader)?; + ext_shm = Some(s); + has_ext = ext; + } + ext::Auth::ID => { + let (a, ext): (ext::Auth, bool) = eodec.read(&mut *reader)?; + ext_auth = Some(a); + has_ext = ext; + } + ext::MultiLink::ID => { + let (a, ext): (ext::MultiLink, bool) = eodec.read(&mut *reader)?; + ext_mlink = Some(a); + has_ext = ext; + } + _ => { + has_ext = extension::skip(reader, "InitAck", ext)?; + } + } + } Ok(InitAck { + version, whatami, zid, - sn_resolution, - is_qos, + resolution, + batch_size, cookie, + ext_qos, + ext_shm, + ext_auth, + ext_mlink, }) } } diff --git a/commons/zenoh-codec/src/transport/join.rs b/commons/zenoh-codec/src/transport/join.rs index b80c55af79..197190946a 100644 --- a/commons/zenoh-codec/src/transport/join.rs +++ b/commons/zenoh-codec/src/transport/join.rs @@ -11,7 +11,7 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::{RCodec, WCodec, Zenoh060, Zenoh060Header}; +use crate::{common::extension, LCodec, RCodec, WCodec, Zenoh080, Zenoh080Header, Zenoh080Length}; use alloc::boxed::Box; use core::time::Duration; use zenoh_buffers::{ @@ -19,142 +19,260 @@ use zenoh_buffers::{ writer::{DidntWrite, Writer}, }; use zenoh_protocol::{ - common::imsg, - core::{ConduitSn, ConduitSnList, Priority, WhatAmI, ZInt, ZenohId}, - defaults::SEQ_NUM_RES, - transport::{tmsg, Join}, + common::{iext, imsg, ZExtZBufHeader}, + core::{Priority, Resolution, WhatAmI, ZenohId}, + transport::{ + batch_size, id, + join::{ext, flag, Join}, + BatchSize, PrioritySn, TransportSn, + }, }; -impl WCodec<&Join, &mut W> for Zenoh060 +impl LCodec<&PrioritySn> for Zenoh080 { + fn w_len(self, p: &PrioritySn) -> usize { + self.w_len(p.reliable) + self.w_len(p.best_effort) + } +} + +impl WCodec<&PrioritySn, &mut W> for Zenoh080 where W: Writer, { type Output = Result<(), DidntWrite>; - fn write(self, writer: &mut W, x: &Join) -> Self::Output { - fn options(x: &Join) -> ZInt { - let mut options = 0; - if x.is_qos() { - options |= tmsg::join_options::QOS; - } - options + fn write(self, writer: &mut W, x: &PrioritySn) -> Self::Output { + self.write(&mut *writer, x.reliable)?; + self.write(&mut *writer, x.best_effort)?; + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let reliable: TransportSn = self.read(&mut *reader)?; + let best_effort: TransportSn = self.read(&mut *reader)?; + + Ok(PrioritySn { + reliable, + best_effort, + }) + } +} + +// Extension +impl WCodec<(&ext::QoSType, bool), &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: (&ext::QoSType, bool)) -> Self::Output { + let (x, more) = x; + + // Header + let len = x.iter().fold(0, |acc, p| acc + self.w_len(p)); + let header = ZExtZBufHeader::<{ ext::QoS::ID }>::new(len); + self.write(&mut *writer, (&header, more))?; + + // Body + for p in x.iter() { + self.write(&mut *writer, p)?; } + Ok(()) + } +} + +impl RCodec<(ext::QoSType, bool), &mut R> for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<(ext::QoSType, bool), Self::Error> { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(reader) + } +} + +impl RCodec<(ext::QoSType, bool), &mut R> for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<(ext::QoSType, bool), Self::Error> { // Header - let mut header = tmsg::id::JOIN; + let (_, more): (ZExtZBufHeader<{ ext::QoS::ID }>, bool) = self.read(&mut *reader)?; + + // Body + let mut ext_qos = Box::new([PrioritySn::default(); Priority::NUM]); + for p in ext_qos.iter_mut() { + *p = self.codec.read(&mut *reader)?; + } + + Ok((ext_qos, more)) + } +} + +// Join +impl WCodec<&Join, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &Join) -> Self::Output { + // Header + let mut header = id::JOIN; if x.lease.as_millis() % 1_000 == 0 { - header |= tmsg::flag::T1; + header |= flag::T; } - if x.sn_resolution != SEQ_NUM_RES { - header |= tmsg::flag::S; + if x.resolution != Resolution::default() || x.batch_size != batch_size::MULTICAST { + header |= flag::S; } - let opts = options(x); - if opts != 0 { - header |= tmsg::flag::O; + let mut n_exts = (x.ext_qos.is_some() as u8) + (x.ext_shm.is_some() as u8); + if n_exts != 0 { + header |= flag::Z; } self.write(&mut *writer, header)?; - if opts != 0 { - self.write(&mut *writer, options(x))?; - } // Body self.write(&mut *writer, x.version)?; - let wai: ZInt = x.whatami.into(); - self.write(&mut *writer, wai)?; - self.write(&mut *writer, &x.zid)?; - if imsg::has_flag(header, tmsg::flag::T1) { - self.write(&mut *writer, x.lease.as_secs() as ZInt)?; + + let whatami: u8 = match x.whatami { + WhatAmI::Router => 0b00, + WhatAmI::Peer => 0b01, + WhatAmI::Client => 0b10, + }; + let flags: u8 = ((x.zid.size() as u8 - 1) << 4) | whatami; + self.write(&mut *writer, flags)?; + + let lodec = Zenoh080Length::new(x.zid.size()); + lodec.write(&mut *writer, &x.zid)?; + + if imsg::has_flag(header, flag::S) { + self.write(&mut *writer, x.resolution.as_u8())?; + self.write(&mut *writer, x.batch_size.to_le_bytes())?; + } + + if imsg::has_flag(header, flag::T) { + self.write(&mut *writer, x.lease.as_secs())?; } else { - self.write(&mut *writer, x.lease.as_millis() as ZInt)?; + self.write(&mut *writer, x.lease.as_millis() as u64)?; } - if imsg::has_flag(header, tmsg::flag::S) { - self.write(&mut *writer, x.sn_resolution)?; + self.write(&mut *writer, &x.next_sn)?; + + // Extensions + if let Some(qos) = x.ext_qos.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (qos, n_exts != 0))?; } - match &x.next_sns { - ConduitSnList::Plain(sn) => { - self.write(&mut *writer, sn.reliable)?; - self.write(&mut *writer, sn.best_effort)?; - } - ConduitSnList::QoS(sns) => { - for sn in sns.iter() { - self.write(&mut *writer, sn.reliable)?; - self.write(&mut *writer, sn.best_effort)?; - } - } + if let Some(shm) = x.ext_shm.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (shm, n_exts != 0))?; } - // true + Ok(()) } } -impl RCodec for Zenoh060 +impl RCodec for Zenoh080 where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); codec.read(reader) } } -impl RCodec for Zenoh060Header +impl RCodec for Zenoh080Header where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - if imsg::mid(self.header) != tmsg::id::JOIN { + if imsg::mid(self.header) != id::JOIN { return Err(DidntRead); } - let options: ZInt = if imsg::has_flag(self.header, tmsg::flag::O) { - self.codec.read(&mut *reader)? - } else { - 0 - }; + // Body let version: u8 = self.codec.read(&mut *reader)?; - let wai: ZInt = self.codec.read(&mut *reader)?; - let whatami = WhatAmI::try_from(wai).ok_or(DidntRead)?; - let zid: ZenohId = self.codec.read(&mut *reader)?; - let lease: ZInt = self.codec.read(&mut *reader)?; - let lease = if imsg::has_flag(self.header, tmsg::flag::T1) { + + let flags: u8 = self.codec.read(&mut *reader)?; + let whatami = match flags & 0b11 { + 0b00 => WhatAmI::Router, + 0b01 => WhatAmI::Peer, + 0b10 => WhatAmI::Client, + _ => return Err(DidntRead), + }; + let length = 1 + ((flags >> 4) as usize); + let lodec = Zenoh080Length::new(length); + let zid: ZenohId = lodec.read(&mut *reader)?; + + let mut resolution = Resolution::default(); + let mut batch_size = batch_size::MULTICAST.to_le_bytes(); + if imsg::has_flag(self.header, flag::S) { + let flags: u8 = self.codec.read(&mut *reader)?; + resolution = Resolution::from(flags & 0b00111111); + batch_size = self.codec.read(&mut *reader)?; + } + let batch_size = BatchSize::from_le_bytes(batch_size); + + let lease: u64 = self.codec.read(&mut *reader)?; + let lease = if imsg::has_flag(self.header, flag::T) { Duration::from_secs(lease) } else { Duration::from_millis(lease) }; - let sn_resolution: ZInt = if imsg::has_flag(self.header, tmsg::flag::S) { - self.codec.read(&mut *reader)? - } else { - SEQ_NUM_RES - }; - let is_qos = imsg::has_option(options, tmsg::init_options::QOS); - let next_sns = if is_qos { - let mut sns = Box::new([ConduitSn::default(); Priority::NUM]); - for i in 0..Priority::NUM { - sns[i].reliable = self.codec.read(&mut *reader)?; - sns[i].best_effort = self.codec.read(&mut *reader)?; + let next_sn: PrioritySn = self.codec.read(&mut *reader)?; + + // Extensions + let mut ext_qos = None; + let mut ext_shm = None; + + let mut has_ext = imsg::has_flag(self.header, flag::Z); + while has_ext { + let ext: u8 = self.codec.read(&mut *reader)?; + let eodec = Zenoh080Header::new(ext); + match iext::eid(ext) { + ext::QoS::ID => { + let (q, ext): (ext::QoSType, bool) = eodec.read(&mut *reader)?; + ext_qos = Some(q); + has_ext = ext; + } + ext::Shm::ID => { + let (s, ext): (ext::Shm, bool) = eodec.read(&mut *reader)?; + ext_shm = Some(s); + has_ext = ext; + } + _ => { + has_ext = extension::skip(reader, "Join", ext)?; + } } - ConduitSnList::QoS(sns) - } else { - ConduitSnList::Plain(ConduitSn { - reliable: self.codec.read(&mut *reader)?, - best_effort: self.codec.read(&mut *reader)?, - }) - }; + } Ok(Join { version, whatami, zid, + resolution, + batch_size, lease, - sn_resolution, - next_sns, + next_sn, + ext_qos, + ext_shm, }) } } diff --git a/commons/zenoh-codec/src/transport/keepalive.rs b/commons/zenoh-codec/src/transport/keepalive.rs index 073bcf0dfc..ce432e63a6 100644 --- a/commons/zenoh-codec/src/transport/keepalive.rs +++ b/commons/zenoh-codec/src/transport/keepalive.rs @@ -11,72 +11,63 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::{RCodec, WCodec, Zenoh060, Zenoh060Header}; +use crate::{common::extension, RCodec, WCodec, Zenoh080, Zenoh080Header}; use zenoh_buffers::{ reader::{DidntRead, Reader}, writer::{DidntWrite, Writer}, }; use zenoh_protocol::{ common::imsg, - core::ZenohId, - transport::{tmsg, KeepAlive}, + transport::{ + id, + keepalive::{flag, KeepAlive}, + }, }; -impl WCodec<&KeepAlive, &mut W> for Zenoh060 +impl WCodec<&KeepAlive, &mut W> for Zenoh080 where W: Writer, { type Output = Result<(), DidntWrite>; - fn write(self, writer: &mut W, x: &KeepAlive) -> Self::Output { + fn write(self, writer: &mut W, _x: &KeepAlive) -> Self::Output { // Header - let mut header = tmsg::id::KEEP_ALIVE; - if x.zid.is_some() { - header |= tmsg::flag::I; - } + let header = id::KEEP_ALIVE; self.write(&mut *writer, header)?; - - // Body - if let Some(p) = x.zid.as_ref() { - self.write(&mut *writer, p)?; - } Ok(()) } } -impl RCodec for Zenoh060 +impl RCodec for Zenoh080 where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); codec.read(reader) } } -impl RCodec for Zenoh060Header +impl RCodec for Zenoh080Header where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - if imsg::mid(self.header) != tmsg::id::KEEP_ALIVE { + if imsg::mid(self.header) != id::KEEP_ALIVE { return Err(DidntRead); } - let zid = if imsg::has_flag(self.header, tmsg::flag::I) { - let zid: ZenohId = self.codec.read(&mut *reader)?; - Some(zid) - } else { - None - }; + // Extensions + let has_ext = imsg::has_flag(self.header, flag::Z); + if has_ext { + extension::skip_all(reader, "Unknown KeepAlive ext")?; + } - Ok(KeepAlive { zid }) + Ok(KeepAlive) } } diff --git a/commons/zenoh-codec/src/transport/mod.rs b/commons/zenoh-codec/src/transport/mod.rs index a14913bdc8..8cb74d7d77 100644 --- a/commons/zenoh-codec/src/transport/mod.rs +++ b/commons/zenoh-codec/src/transport/mod.rs @@ -12,85 +12,162 @@ // ZettaScale Zenoh Team, // mod close; +mod fragment; mod frame; mod init; mod join; mod keepalive; +mod oam; mod open; -use crate::{RCodec, WCodec, Zenoh060, Zenoh060Header}; +use crate::{RCodec, WCodec, Zenoh080, Zenoh080Header}; use zenoh_buffers::{ reader::{BacktrackableReader, DidntRead, Reader}, writer::{DidntWrite, Writer}, }; +#[cfg(feature = "shared-memory")] +use zenoh_protocol::network::NetworkMessage; use zenoh_protocol::{ - common::{imsg, Attachment}, + common::{imsg, ZExtZ64}, transport::*, }; +// TransportMessageShm +#[cfg(feature = "shared-memory")] +impl WCodec<&TransportMessageShm, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &TransportMessageShm) -> Self::Output { + match &x.body { + TransportBodyShm::Network(b) => self.write(&mut *writer, b.as_ref()), + TransportBodyShm::KeepAlive(b) => self.write(&mut *writer, b), + TransportBodyShm::Close(b) => self.write(&mut *writer, b), + } + } +} +#[cfg(feature = "shared-memory")] +impl RCodec for Zenoh080 +where + R: Reader + BacktrackableReader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + + let codec = Zenoh080Header::new(header); + let body = match imsg::mid(codec.header) { + id::KEEP_ALIVE => TransportBodyShm::KeepAlive(codec.read(&mut *reader)?), + id::CLOSE => TransportBodyShm::Close(codec.read(&mut *reader)?), + _ => { + let nw: NetworkMessage = codec.read(&mut *reader)?; + TransportBodyShm::Network(Box::new(nw)) + } + }; + + Ok(TransportMessageShm { body }) + } +} + // TransportMessage -impl WCodec<&TransportMessage, &mut W> for Zenoh060 +impl WCodec<&TransportMessage, &mut W> for Zenoh080 where W: Writer, { type Output = Result<(), DidntWrite>; fn write(self, writer: &mut W, x: &TransportMessage) -> Self::Output { - if let Some(a) = x.attachment.as_ref() { - self.write(&mut *writer, a)?; - } match &x.body { + TransportBody::Frame(b) => self.write(&mut *writer, b), + TransportBody::Fragment(b) => self.write(&mut *writer, b), + TransportBody::KeepAlive(b) => self.write(&mut *writer, b), TransportBody::InitSyn(b) => self.write(&mut *writer, b), TransportBody::InitAck(b) => self.write(&mut *writer, b), TransportBody::OpenSyn(b) => self.write(&mut *writer, b), TransportBody::OpenAck(b) => self.write(&mut *writer, b), - TransportBody::Join(b) => self.write(&mut *writer, b), TransportBody::Close(b) => self.write(&mut *writer, b), - TransportBody::KeepAlive(b) => self.write(&mut *writer, b), - TransportBody::Frame(b) => self.write(&mut *writer, b), + TransportBody::OAM(b) => self.write(&mut *writer, b), + TransportBody::Join(b) => self.write(&mut *writer, b), } } } -impl RCodec for Zenoh060 +impl RCodec for Zenoh080 where R: Reader + BacktrackableReader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - let mut codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; - let mut attachment: Option = None; - if imsg::mid(codec.header) == tmsg::id::ATTACHMENT { - let a: Attachment = codec.read(&mut *reader)?; - attachment = Some(a); - codec.header = self.read(&mut *reader)?; - } + let header: u8 = self.read(&mut *reader)?; + + let codec = Zenoh080Header::new(header); let body = match imsg::mid(codec.header) { - tmsg::id::INIT => { - if !imsg::has_flag(codec.header, tmsg::flag::A) { + id::FRAME => TransportBody::Frame(codec.read(&mut *reader)?), + id::FRAGMENT => TransportBody::Fragment(codec.read(&mut *reader)?), + id::KEEP_ALIVE => TransportBody::KeepAlive(codec.read(&mut *reader)?), + id::INIT => { + if !imsg::has_flag(codec.header, zenoh_protocol::transport::init::flag::A) { TransportBody::InitSyn(codec.read(&mut *reader)?) } else { TransportBody::InitAck(codec.read(&mut *reader)?) } } - tmsg::id::OPEN => { - if !imsg::has_flag(codec.header, tmsg::flag::A) { + id::OPEN => { + if !imsg::has_flag(codec.header, zenoh_protocol::transport::open::flag::A) { TransportBody::OpenSyn(codec.read(&mut *reader)?) } else { TransportBody::OpenAck(codec.read(&mut *reader)?) } } - tmsg::id::JOIN => TransportBody::Join(codec.read(&mut *reader)?), - tmsg::id::CLOSE => TransportBody::Close(codec.read(&mut *reader)?), - tmsg::id::KEEP_ALIVE => TransportBody::KeepAlive(codec.read(&mut *reader)?), - tmsg::id::PRIORITY | tmsg::id::FRAME => TransportBody::Frame(codec.read(&mut *reader)?), + id::CLOSE => TransportBody::Close(codec.read(&mut *reader)?), + id::OAM => TransportBody::OAM(codec.read(&mut *reader)?), + id::JOIN => TransportBody::Join(codec.read(&mut *reader)?), _ => return Err(DidntRead), }; - Ok(TransportMessage { body, attachment }) + Ok(body.into()) + } +} + +// Extensions: QoS +impl WCodec<(ext::QoSType<{ ID }>, bool), &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: (ext::QoSType<{ ID }>, bool)) -> Self::Output { + let (x, more) = x; + let ext: ZExtZ64<{ ID }> = x.into(); + self.write(&mut *writer, (&ext, more)) + } +} + +impl RCodec<(ext::QoSType<{ ID }>, bool), &mut R> for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<(ext::QoSType<{ ID }>, bool), Self::Error> { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(reader) + } +} + +impl RCodec<(ext::QoSType<{ ID }>, bool), &mut R> for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<(ext::QoSType<{ ID }>, bool), Self::Error> { + let (ext, more): (ZExtZ64<{ ID }>, bool) = self.read(&mut *reader)?; + Ok((ext.into(), more)) } } diff --git a/commons/zenoh-codec/src/transport/oam.rs b/commons/zenoh-codec/src/transport/oam.rs new file mode 100644 index 0000000000..46fe63345e --- /dev/null +++ b/commons/zenoh-codec/src/transport/oam.rs @@ -0,0 +1,140 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::{common::extension, RCodec, WCodec, Zenoh080, Zenoh080Header}; +use zenoh_buffers::{ + reader::{DidntRead, Reader}, + writer::{DidntWrite, Writer}, + ZBuf, +}; +use zenoh_protocol::{ + common::{iext, imsg, ZExtBody}, + transport::{ + id, + oam::{ext, flag, Oam, OamId}, + }, +}; + +impl WCodec<&Oam, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &Oam) -> Self::Output { + // Header + let mut header = id::OAM; + match &x.body { + ZExtBody::Unit => { + header |= iext::ENC_UNIT; + } + ZExtBody::Z64(_) => { + header |= iext::ENC_Z64; + } + ZExtBody::ZBuf(_) => { + header |= iext::ENC_ZBUF; + } + } + let mut n_exts = (x.ext_qos != ext::QoSType::default()) as u8; + if n_exts != 0 { + header |= flag::Z; + } + self.write(&mut *writer, header)?; + + // Body + self.write(&mut *writer, x.id)?; + + // Extensions + if x.ext_qos != ext::QoSType::default() { + n_exts -= 1; + self.write(&mut *writer, (x.ext_qos, n_exts != 0))?; + } + + // Payload + match &x.body { + ZExtBody::Unit => {} + ZExtBody::Z64(u64) => { + self.write(&mut *writer, u64)?; + } + ZExtBody::ZBuf(zbuf) => { + self.write(&mut *writer, zbuf)?; + } + } + + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(reader) + } +} + +impl RCodec for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + if imsg::mid(self.header) != id::OAM { + return Err(DidntRead); + } + + // Body + let id: OamId = self.codec.read(&mut *reader)?; + + // Extensions + let mut ext_qos = ext::QoSType::default(); + + let mut has_ext = imsg::has_flag(self.header, flag::Z); + while has_ext { + let ext: u8 = self.codec.read(&mut *reader)?; + let eodec = Zenoh080Header::new(ext); + match iext::eid(ext) { + ext::QoS::ID => { + let (q, ext): (ext::QoSType, bool) = eodec.read(&mut *reader)?; + ext_qos = q; + has_ext = ext; + } + _ => { + has_ext = extension::skip(reader, "OAM", ext)?; + } + } + } + + // Payload + let body = match self.header & iext::ENC_MASK { + iext::ENC_UNIT => ZExtBody::Unit, + iext::ENC_Z64 => { + let u64: u64 = self.codec.read(&mut *reader)?; + ZExtBody::Z64(u64) + } + iext::ENC_ZBUF => { + let zbuf: ZBuf = self.codec.read(&mut *reader)?; + ZExtBody::ZBuf(zbuf) + } + _ => return Err(DidntRead), + }; + + Ok(Oam { id, body, ext_qos }) + } +} diff --git a/commons/zenoh-codec/src/transport/open.rs b/commons/zenoh-codec/src/transport/open.rs index ef49c86005..62f0c65d1f 100644 --- a/commons/zenoh-codec/src/transport/open.rs +++ b/commons/zenoh-codec/src/transport/open.rs @@ -11,7 +11,7 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::{RCodec, WCodec, Zenoh060, Zenoh060Header}; +use crate::{common::extension, RCodec, WCodec, Zenoh080, Zenoh080Header}; use core::time::Duration; use zenoh_buffers::{ reader::{DidntRead, Reader}, @@ -19,13 +19,16 @@ use zenoh_buffers::{ ZSlice, }; use zenoh_protocol::{ - common::imsg, - core::ZInt, - transport::{tmsg, OpenAck, OpenSyn}, + common::{iext, imsg}, + transport::{ + id, + open::{ext, flag, OpenAck, OpenSyn}, + TransportSn, + }, }; // OpenSyn -impl WCodec<&OpenSyn, &mut W> for Zenoh060 +impl WCodec<&OpenSyn, &mut W> for Zenoh080 where W: Writer, { @@ -33,69 +36,135 @@ where fn write(self, writer: &mut W, x: &OpenSyn) -> Self::Output { // Header - let mut header = tmsg::id::OPEN; + let mut header = id::OPEN; if x.lease.as_millis() % 1_000 == 0 { - header |= tmsg::flag::T2; + header |= flag::T; + } + let mut n_exts = (x.ext_qos.is_some() as u8) + + (x.ext_shm.is_some() as u8) + + (x.ext_auth.is_some() as u8) + + (x.ext_mlink.is_some() as u8); + if n_exts != 0 { + header |= flag::Z; } self.write(&mut *writer, header)?; // Body - if imsg::has_flag(header, tmsg::flag::T2) { - self.write(&mut *writer, x.lease.as_secs() as ZInt)?; + if imsg::has_flag(header, flag::T) { + self.write(&mut *writer, x.lease.as_secs())?; } else { - self.write(&mut *writer, x.lease.as_millis() as ZInt)?; + self.write(&mut *writer, x.lease.as_millis() as u64)?; } self.write(&mut *writer, x.initial_sn)?; self.write(&mut *writer, &x.cookie)?; + + // Extensions + if let Some(qos) = x.ext_qos.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (qos, n_exts != 0))?; + } + if let Some(shm) = x.ext_shm.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (shm, n_exts != 0))?; + } + if let Some(auth) = x.ext_auth.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (auth, n_exts != 0))?; + } + if let Some(mlink) = x.ext_mlink.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (mlink, n_exts != 0))?; + } + Ok(()) } } -impl RCodec for Zenoh060 +impl RCodec for Zenoh080 where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); codec.read(reader) } } -impl RCodec for Zenoh060Header +impl RCodec for Zenoh080Header where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - if imsg::mid(self.header) != imsg::id::OPEN || imsg::has_flag(self.header, tmsg::flag::A) { + if imsg::mid(self.header) != id::OPEN || imsg::has_flag(self.header, flag::A) { return Err(DidntRead); } - let lease: ZInt = self.codec.read(&mut *reader)?; - let lease = if imsg::has_flag(self.header, tmsg::flag::T2) { + // Body + let lease: u64 = self.codec.read(&mut *reader)?; + let lease = if imsg::has_flag(self.header, flag::T) { Duration::from_secs(lease) } else { Duration::from_millis(lease) }; - let initial_sn: ZInt = self.codec.read(&mut *reader)?; + let initial_sn: TransportSn = self.codec.read(&mut *reader)?; let cookie: ZSlice = self.codec.read(&mut *reader)?; + // Extensions + let mut ext_qos = None; + let mut ext_shm = None; + let mut ext_auth = None; + let mut ext_mlink = None; + + let mut has_ext = imsg::has_flag(self.header, flag::Z); + while has_ext { + let ext: u8 = self.codec.read(&mut *reader)?; + let eodec = Zenoh080Header::new(ext); + match iext::eid(ext) { + ext::QoS::ID => { + let (q, ext): (ext::QoS, bool) = eodec.read(&mut *reader)?; + ext_qos = Some(q); + has_ext = ext; + } + ext::Shm::ID => { + let (s, ext): (ext::Shm, bool) = eodec.read(&mut *reader)?; + ext_shm = Some(s); + has_ext = ext; + } + ext::Auth::ID => { + let (a, ext): (ext::Auth, bool) = eodec.read(&mut *reader)?; + ext_auth = Some(a); + has_ext = ext; + } + ext::MultiLinkSyn::ID => { + let (a, ext): (ext::MultiLinkSyn, bool) = eodec.read(&mut *reader)?; + ext_mlink = Some(a); + has_ext = ext; + } + _ => { + has_ext = extension::skip(reader, "OpenSyn", ext)?; + } + } + } + Ok(OpenSyn { lease, initial_sn, cookie, + ext_qos, + ext_shm, + ext_auth, + ext_mlink, }) } } // OpenAck -impl WCodec<&OpenAck, &mut W> for Zenoh060 +impl WCodec<&OpenAck, &mut W> for Zenoh080 where W: Writer, { @@ -103,59 +172,128 @@ where fn write(self, writer: &mut W, x: &OpenAck) -> Self::Output { // Header - let mut header = tmsg::id::OPEN; - header |= tmsg::flag::A; + let mut header = id::OPEN; + header |= flag::A; // Verify that the timeout is expressed in seconds, i.e. subsec part is 0. if x.lease.subsec_nanos() == 0 { - header |= tmsg::flag::T2; + header |= flag::T; + } + let mut n_exts = (x.ext_qos.is_some() as u8) + + (x.ext_shm.is_some() as u8) + + (x.ext_auth.is_some() as u8) + + (x.ext_mlink.is_some() as u8); + if n_exts != 0 { + header |= flag::Z; } self.write(&mut *writer, header)?; // Body - if imsg::has_flag(header, tmsg::flag::T2) { - self.write(&mut *writer, x.lease.as_secs() as ZInt)?; + if imsg::has_flag(header, flag::T) { + self.write(&mut *writer, x.lease.as_secs())?; } else { - self.write(&mut *writer, x.lease.as_millis() as ZInt)?; + self.write(&mut *writer, x.lease.as_millis() as u64)?; } self.write(&mut *writer, x.initial_sn)?; + + // Extensions + if let Some(qos) = x.ext_qos.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (qos, n_exts != 0))?; + } + if let Some(shm) = x.ext_shm.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (shm, n_exts != 0))?; + } + if let Some(auth) = x.ext_auth.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (auth, n_exts != 0))?; + } + if let Some(mlink) = x.ext_mlink.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (mlink, n_exts != 0))?; + } + Ok(()) } } -impl RCodec for Zenoh060 +impl RCodec for Zenoh080 where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); codec.read(reader) } } -impl RCodec for Zenoh060Header +impl RCodec for Zenoh080Header where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - if imsg::mid(self.header) != tmsg::id::OPEN || !imsg::has_flag(self.header, tmsg::flag::A) { + if imsg::mid(self.header) != id::OPEN || !imsg::has_flag(self.header, flag::A) { return Err(DidntRead); } - let lease: ZInt = self.codec.read(&mut *reader)?; - let lease = if imsg::has_flag(self.header, tmsg::flag::T2) { + // Body + let lease: u64 = self.codec.read(&mut *reader)?; + let lease = if imsg::has_flag(self.header, flag::T) { Duration::from_secs(lease) } else { Duration::from_millis(lease) }; - let initial_sn: ZInt = self.codec.read(&mut *reader)?; + let initial_sn: TransportSn = self.codec.read(&mut *reader)?; - Ok(OpenAck { lease, initial_sn }) + // Extensions + let mut ext_qos = None; + let mut ext_shm = None; + let mut ext_auth = None; + let mut ext_mlink = None; + + let mut has_ext = imsg::has_flag(self.header, flag::Z); + while has_ext { + let ext: u8 = self.codec.read(&mut *reader)?; + let eodec = Zenoh080Header::new(ext); + match iext::eid(ext) { + ext::QoS::ID => { + let (q, ext): (ext::QoS, bool) = eodec.read(&mut *reader)?; + ext_qos = Some(q); + has_ext = ext; + } + ext::Shm::ID => { + let (s, ext): (ext::Shm, bool) = eodec.read(&mut *reader)?; + ext_shm = Some(s); + has_ext = ext; + } + ext::Auth::ID => { + let (a, ext): (ext::Auth, bool) = eodec.read(&mut *reader)?; + ext_auth = Some(a); + has_ext = ext; + } + ext::MultiLinkAck::ID => { + let (a, ext): (ext::MultiLinkAck, bool) = eodec.read(&mut *reader)?; + ext_mlink = Some(a); + has_ext = ext; + } + _ => { + has_ext = extension::skip(reader, "OpenAck", ext)?; + } + } + } + + Ok(OpenAck { + lease, + initial_sn, + ext_qos, + ext_shm, + ext_auth, + ext_mlink, + }) } } diff --git a/commons/zenoh-codec/src/zenoh/ack.rs b/commons/zenoh-codec/src/zenoh/ack.rs new file mode 100644 index 0000000000..0b940eb877 --- /dev/null +++ b/commons/zenoh-codec/src/zenoh/ack.rs @@ -0,0 +1,123 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::{common::extension, RCodec, WCodec, Zenoh080, Zenoh080Header}; +use alloc::vec::Vec; +use zenoh_buffers::{ + reader::{DidntRead, Reader}, + writer::{DidntWrite, Writer}, +}; +use zenoh_protocol::{ + common::{iext, imsg}, + zenoh::{ + ack::{ext, flag, Ack}, + id, + }, +}; + +impl WCodec<&Ack, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &Ack) -> Self::Output { + // Header + let mut header = id::ACK; + if x.timestamp.is_some() { + header |= flag::T; + } + let mut n_exts = ((x.ext_sinfo.is_some()) as u8) + (x.ext_unknown.len() as u8); + if n_exts != 0 { + header |= flag::Z; + } + self.write(&mut *writer, header)?; + + // Body + if let Some(ts) = x.timestamp.as_ref() { + self.write(&mut *writer, ts)?; + } + + // Extensions + if let Some(sinfo) = x.ext_sinfo.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (sinfo, n_exts != 0))?; + } + for u in x.ext_unknown.iter() { + n_exts -= 1; + self.write(&mut *writer, (u, n_exts != 0))?; + } + + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(reader) + } +} + +impl RCodec for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + if imsg::mid(self.header) != id::ACK { + return Err(DidntRead); + } + + // Body + let mut timestamp: Option = None; + if imsg::has_flag(self.header, flag::T) { + timestamp = Some(self.codec.read(&mut *reader)?); + } + + // Extensions + let mut ext_sinfo: Option = None; + let mut ext_unknown = Vec::new(); + + let mut has_ext = imsg::has_flag(self.header, flag::Z); + while has_ext { + let ext: u8 = self.codec.read(&mut *reader)?; + let eodec = Zenoh080Header::new(ext); + match iext::eid(ext) { + ext::SourceInfo::ID => { + let (s, ext): (ext::SourceInfoType, bool) = eodec.read(&mut *reader)?; + ext_sinfo = Some(s); + has_ext = ext; + } + _ => { + let (u, ext) = extension::read(reader, "Ack", ext)?; + ext_unknown.push(u); + has_ext = ext; + } + } + } + + Ok(Ack { + timestamp, + ext_sinfo, + ext_unknown, + }) + } +} diff --git a/commons/zenoh-codec/src/zenoh/data.rs b/commons/zenoh-codec/src/zenoh/data.rs deleted file mode 100644 index 6eba98c91e..0000000000 --- a/commons/zenoh-codec/src/zenoh/data.rs +++ /dev/null @@ -1,314 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use crate::{ - RCodec, WCodec, Zenoh060, Zenoh060Condition, Zenoh060Header, Zenoh060HeaderReplyContext, -}; -use core::convert::TryInto; -use uhlc::Timestamp; -use zenoh_buffers::{ - reader::{DidntRead, Reader}, - writer::{DidntWrite, Writer}, - ZBuf, -}; -use zenoh_protocol::{ - common::imsg, - core::{CongestionControl, Encoding, SampleKind, WireExpr, ZInt, ZenohId}, - zenoh::{zmsg, Data, DataInfo, ReplierInfo, ReplyContext}, -}; - -// ReplyContext -impl WCodec<&ReplyContext, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &ReplyContext) -> Self::Output { - // Header - let mut header = zmsg::id::REPLY_CONTEXT; - if x.is_final() { - header |= zmsg::flag::F; - } - self.write(&mut *writer, header)?; - - // Body - self.write(&mut *writer, x.qid)?; - if let Some(replier) = x.replier.as_ref() { - self.write(&mut *writer, &replier.id)?; - } - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; - codec.read(reader) - } -} - -impl RCodec for Zenoh060Header -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - if imsg::mid(self.header) != zmsg::id::REPLY_CONTEXT { - return Err(DidntRead); - } - - let qid: ZInt = self.codec.read(&mut *reader)?; - let replier = if imsg::has_flag(self.header, zmsg::flag::F) { - None - } else { - let id: ZenohId = self.codec.read(&mut *reader)?; - Some(ReplierInfo { id }) - }; - Ok(ReplyContext { qid, replier }) - } -} - -// DataInfo -impl WCodec<&DataInfo, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &DataInfo) -> Self::Output { - // Options - let mut options = 0; - #[cfg(feature = "shared-memory")] - if x.sliced { - options |= zmsg::data::info::SLICED; - } - if x.kind != SampleKind::Put { - options |= zmsg::data::info::KIND; - } - if x.encoding.is_some() { - options |= zmsg::data::info::ENCODING; - } - if x.timestamp.is_some() { - options |= zmsg::data::info::TIMESTAMP; - } - if x.source_id.is_some() { - options |= zmsg::data::info::SRCID; - } - if x.source_sn.is_some() { - options |= zmsg::data::info::SRCSN; - } - self.write(&mut *writer, options)?; - - if x.kind != SampleKind::Put { - self.write(&mut *writer, x.kind as ZInt)?; - } - if let Some(enc) = x.encoding.as_ref() { - self.write(&mut *writer, enc)?; - } - if let Some(ts) = x.timestamp.as_ref() { - self.write(&mut *writer, ts)?; - } - if let Some(si) = x.source_id.as_ref() { - self.write(&mut *writer, si)?; - } - if let Some(sn) = x.source_sn { - self.write(&mut *writer, sn)?; - } - - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let mut info = DataInfo::default(); - let options: ZInt = self.read(&mut *reader)?; - #[cfg(feature = "shared-memory")] - { - info.sliced = imsg::has_option(options, zmsg::data::info::SLICED); - } - if imsg::has_option(options, zmsg::data::info::KIND) { - let kind: ZInt = self.read(&mut *reader)?; - info.kind = kind.try_into().map_err(|_| DidntRead)?; - } - if imsg::has_option(options, zmsg::data::info::ENCODING) { - let encoding: Encoding = self.read(&mut *reader)?; - info.encoding = Some(encoding); - } - if imsg::has_option(options, zmsg::data::info::TIMESTAMP) { - let timestamp: Timestamp = self.read(&mut *reader)?; - info.timestamp = Some(timestamp); - } - if imsg::has_option(options, zmsg::data::info::SRCID) { - let source_id: ZenohId = self.read(&mut *reader)?; - info.source_id = Some(source_id); - } - if imsg::has_option(options, zmsg::data::info::SRCSN) { - let source_sn: ZInt = self.read(&mut *reader)?; - info.source_sn = Some(source_sn); - } - - Ok(info) - } -} - -// Data -impl WCodec<&Data, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &Data) -> Self::Output { - if let Some(reply_context) = x.reply_context.as_ref() { - self.write(&mut *writer, reply_context)?; - } - - // Header - let mut header = zmsg::id::DATA; - if x.data_info.is_some() { - header |= zmsg::flag::I; - } - if x.key.has_suffix() { - header |= zmsg::flag::K; - } - if x.congestion_control == CongestionControl::Drop { - header |= zmsg::flag::D; - } - self.write(&mut *writer, header)?; - - // Body - self.write(&mut *writer, &x.key)?; - - #[cfg(feature = "shared-memory")] - let mut sliced = false; - - if let Some(data_info) = x.data_info.as_ref() { - self.write(&mut *writer, data_info)?; - #[cfg(feature = "shared-memory")] - { - sliced = data_info.sliced; - } - } - - #[cfg(feature = "shared-memory")] - { - let codec = Zenoh060Condition::new(sliced); - codec.write(&mut *writer, &x.payload)?; - } - - #[cfg(not(feature = "shared-memory"))] - { - self.write(&mut *writer, &x.payload)?; - } - - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let mut codec = Zenoh060HeaderReplyContext { - header: self.read(&mut *reader)?, - ..Default::default() - }; - if imsg::mid(codec.header) == zmsg::id::REPLY_CONTEXT { - let hodec = Zenoh060Header { - header: codec.header, - ..Default::default() - }; - codec.reply_context = Some(hodec.read(&mut *reader)?); - codec.header = self.read(&mut *reader)?; - } - codec.read(reader) - } -} - -impl RCodec for Zenoh060HeaderReplyContext -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - if imsg::mid(self.header) != zmsg::id::DATA { - return Err(DidntRead); - } - - let congestion_control = if imsg::has_flag(self.header, zmsg::flag::D) { - CongestionControl::Drop - } else { - CongestionControl::Block - }; - - let ccond = Zenoh060Condition { - condition: imsg::has_flag(self.header, zmsg::flag::K), - codec: self.codec, - }; - let key: WireExpr<'static> = ccond.read(&mut *reader)?; - - #[cfg(feature = "shared-memory")] - let mut is_sliced = false; - - let data_info = if imsg::has_flag(self.header, zmsg::flag::I) { - let di: DataInfo = self.codec.read(&mut *reader)?; - #[cfg(feature = "shared-memory")] - { - is_sliced = di.sliced; - } - Some(di) - } else { - None - }; - - let payload: ZBuf = { - #[cfg(feature = "shared-memory")] - { - let codec = Zenoh060Condition::new(is_sliced); - codec.read(&mut *reader)? - } - #[cfg(not(feature = "shared-memory"))] - { - self.codec.read(&mut *reader)? - } - }; - - Ok(Data { - key, - data_info, - payload, - congestion_control, - reply_context: self.reply_context, - }) - } -} diff --git a/commons/zenoh-codec/src/zenoh/declare.rs b/commons/zenoh-codec/src/zenoh/declare.rs deleted file mode 100644 index 1f505f1bb8..0000000000 --- a/commons/zenoh-codec/src/zenoh/declare.rs +++ /dev/null @@ -1,703 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use crate::{RCodec, WCodec, Zenoh060, Zenoh060Condition, Zenoh060Header}; -use alloc::vec::Vec; -use zenoh_buffers::{ - reader::{DidntRead, Reader}, - writer::{DidntWrite, Writer}, -}; -use zenoh_protocol::{ - common::imsg, - core::{QueryableInfo, Reliability, SubInfo, SubMode, WireExpr, ZInt}, - zenoh::{ - zmsg, Declaration, Declare, ForgetPublisher, ForgetQueryable, ForgetResource, - ForgetSubscriber, Publisher, Queryable, Resource, Subscriber, - }, -}; - -// Declaration -impl WCodec<&Declaration, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &Declaration) -> Self::Output { - match x { - Declaration::Resource(r) => self.write(&mut *writer, r)?, - Declaration::ForgetResource(r) => self.write(&mut *writer, r)?, - Declaration::Publisher(r) => self.write(&mut *writer, r)?, - Declaration::ForgetPublisher(r) => self.write(&mut *writer, r)?, - Declaration::Subscriber(r) => self.write(&mut *writer, r)?, - Declaration::ForgetSubscriber(r) => self.write(&mut *writer, r)?, - Declaration::Queryable(r) => self.write(&mut *writer, r)?, - Declaration::ForgetQueryable(r) => self.write(&mut *writer, r)?, - } - - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - use super::zmsg::declaration::id::*; - - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; - - let d = match imsg::mid(codec.header) { - RESOURCE => Declaration::Resource(codec.read(&mut *reader)?), - FORGET_RESOURCE => Declaration::ForgetResource(codec.read(&mut *reader)?), - PUBLISHER => Declaration::Publisher(codec.read(&mut *reader)?), - FORGET_PUBLISHER => Declaration::ForgetPublisher(codec.read(&mut *reader)?), - SUBSCRIBER => Declaration::Subscriber(codec.read(&mut *reader)?), - FORGET_SUBSCRIBER => Declaration::ForgetSubscriber(codec.read(&mut *reader)?), - QUERYABLE => Declaration::Queryable(codec.read(&mut *reader)?), - FORGET_QUERYABLE => Declaration::ForgetQueryable(codec.read(&mut *reader)?), - _ => return Err(DidntRead), - }; - - Ok(d) - } -} - -// Declare -impl WCodec<&Declare, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &Declare) -> Self::Output { - // Header - let header = zmsg::id::DECLARE; - self.write(&mut *writer, header)?; - - // Body - self.write(&mut *writer, x.declarations.len())?; - for d in x.declarations.iter() { - self.write(&mut *writer, d)?; - } - - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; - codec.read(reader) - } -} - -impl RCodec for Zenoh060Header -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - if imsg::mid(self.header) != zmsg::id::DECLARE { - return Err(DidntRead); - } - - let len: usize = self.codec.read(&mut *reader)?; - let mut declarations = Vec::with_capacity(len); - for _ in 0..len { - let d: Declaration = self.codec.read(&mut *reader)?; - declarations.push(d); - } - - Ok(Declare { declarations }) - } -} - -// Resource -impl WCodec<&Resource, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &Resource) -> Self::Output { - // Header - let mut header = zmsg::declaration::id::RESOURCE; - if x.key.has_suffix() { - header |= zmsg::flag::K; - } - self.write(&mut *writer, header)?; - - // Body - self.write(&mut *writer, x.expr_id)?; - self.write(&mut *writer, &x.key)?; - - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; - codec.read(reader) - } -} - -impl RCodec for Zenoh060Header -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - if imsg::mid(self.header) != zmsg::declaration::id::RESOURCE { - return Err(DidntRead); - } - - let expr_id: ZInt = self.codec.read(&mut *reader)?; - let ccond = Zenoh060Condition { - condition: imsg::has_flag(self.header, zmsg::flag::K), - codec: self.codec, - }; - let key: WireExpr<'static> = ccond.read(&mut *reader)?; - - Ok(Resource { expr_id, key }) - } -} - -// ForgetResource -impl WCodec<&ForgetResource, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &ForgetResource) -> Self::Output { - // Header - let header = zmsg::declaration::id::FORGET_RESOURCE; - self.write(&mut *writer, header)?; - - // Body - self.write(&mut *writer, x.expr_id)?; - - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; - codec.read(reader) - } -} - -impl RCodec for Zenoh060Header -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - if imsg::mid(self.header) != zmsg::declaration::id::FORGET_RESOURCE { - return Err(DidntRead); - } - - let expr_id: ZInt = self.codec.read(&mut *reader)?; - - Ok(ForgetResource { expr_id }) - } -} - -// Publisher -impl WCodec<&Publisher, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &Publisher) -> Self::Output { - // Header - let mut header = zmsg::declaration::id::PUBLISHER; - if x.key.has_suffix() { - header |= zmsg::flag::K; - } - self.write(&mut *writer, header)?; - - // Body - self.write(&mut *writer, &x.key)?; - - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; - codec.read(reader) - } -} - -impl RCodec for Zenoh060Header -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - if imsg::mid(self.header) != zmsg::declaration::id::PUBLISHER { - return Err(DidntRead); - } - - let ccond = Zenoh060Condition { - condition: imsg::has_flag(self.header, zmsg::flag::K), - codec: self.codec, - }; - let key: WireExpr<'static> = ccond.read(&mut *reader)?; - - Ok(Publisher { key }) - } -} - -// ForgetPublisher -impl WCodec<&ForgetPublisher, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &ForgetPublisher) -> Self::Output { - // Header - let mut header = zmsg::declaration::id::FORGET_PUBLISHER; - if x.key.has_suffix() { - header |= zmsg::flag::K; - } - self.write(&mut *writer, header)?; - - // Body - self.write(&mut *writer, &x.key)?; - - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; - codec.read(reader) - } -} - -impl RCodec for Zenoh060Header -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - if imsg::mid(self.header) != zmsg::declaration::id::FORGET_PUBLISHER { - return Err(DidntRead); - } - - let ccond = Zenoh060Condition { - condition: imsg::has_flag(self.header, zmsg::flag::K), - codec: self.codec, - }; - let key: WireExpr<'static> = ccond.read(&mut *reader)?; - - Ok(ForgetPublisher { key }) - } -} - -// SubMode -impl WCodec<&SubMode, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &SubMode) -> Self::Output { - // Header - let header = match x { - SubMode::Push => zmsg::declaration::id::MODE_PUSH, - SubMode::Pull => zmsg::declaration::id::MODE_PULL, - }; - self.write(&mut *writer, header)?; - - // Body - - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let header: u8 = self.read(&mut *reader)?; - - let mode = match header { - zmsg::declaration::id::MODE_PUSH => SubMode::Push, - zmsg::declaration::id::MODE_PULL => SubMode::Pull, - _ => return Err(DidntRead), - }; - - Ok(mode) - } -} - -// Subscriber -impl WCodec<&Subscriber, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &Subscriber) -> Self::Output { - // Header - let mut header = zmsg::declaration::id::SUBSCRIBER; - if x.info.reliability == Reliability::Reliable { - header |= zmsg::flag::R; - } - if x.info.mode != SubMode::Push { - header |= zmsg::flag::S; - } - if x.key.has_suffix() { - header |= zmsg::flag::K; - } - self.write(&mut *writer, header)?; - - // Body - self.write(&mut *writer, &x.key)?; - if imsg::has_flag(header, zmsg::flag::S) { - self.write(&mut *writer, &x.info.mode)?; - } - - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; - codec.read(reader) - } -} - -impl RCodec for Zenoh060Header -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - if imsg::mid(self.header) != zmsg::declaration::id::SUBSCRIBER { - return Err(DidntRead); - } - - let reliability = if imsg::has_flag(self.header, zmsg::flag::R) { - Reliability::Reliable - } else { - Reliability::BestEffort - }; - - let ccond = Zenoh060Condition { - condition: imsg::has_flag(self.header, zmsg::flag::K), - codec: self.codec, - }; - let key: WireExpr<'static> = ccond.read(&mut *reader)?; - - let mode: SubMode = if imsg::has_flag(self.header, zmsg::flag::S) { - self.codec.read(&mut *reader)? - } else { - SubMode::Push - }; - - Ok(Subscriber { - key, - info: SubInfo { reliability, mode }, - }) - } -} - -// ForgetSubscriber -impl WCodec<&ForgetSubscriber, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &ForgetSubscriber) -> Self::Output { - // Header - let mut header = zmsg::declaration::id::FORGET_SUBSCRIBER; - if x.key.has_suffix() { - header |= zmsg::flag::K; - } - self.write(&mut *writer, header)?; - - // Body - self.write(&mut *writer, &x.key)?; - - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; - codec.read(reader) - } -} - -impl RCodec for Zenoh060Header -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - if imsg::mid(self.header) != zmsg::declaration::id::FORGET_SUBSCRIBER { - return Err(DidntRead); - } - - let ccond = Zenoh060Condition { - condition: imsg::has_flag(self.header, zmsg::flag::K), - codec: self.codec, - }; - let key: WireExpr<'static> = ccond.read(&mut *reader)?; - - Ok(ForgetSubscriber { key }) - } -} - -// QueryableInfo -impl WCodec<&QueryableInfo, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &QueryableInfo) -> Self::Output { - self.write(&mut *writer, x.complete)?; - self.write(&mut *writer, x.distance)?; - - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let complete: ZInt = self.read(&mut *reader)?; - let distance: ZInt = self.read(&mut *reader)?; - - Ok(QueryableInfo { complete, distance }) - } -} - -// Queryable -impl WCodec<&Queryable, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &Queryable) -> Self::Output { - // Header - let mut header = zmsg::declaration::id::QUERYABLE; - if x.info != QueryableInfo::default() { - header |= zmsg::flag::Q; - } - if x.key.has_suffix() { - header |= zmsg::flag::K; - } - self.write(&mut *writer, header)?; - - // Body - self.write(&mut *writer, &x.key)?; - if imsg::has_flag(header, zmsg::flag::Q) { - self.write(&mut *writer, &x.info)?; - } - - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; - codec.read(reader) - } -} - -impl RCodec for Zenoh060Header -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - if imsg::mid(self.header) != zmsg::declaration::id::QUERYABLE { - return Err(DidntRead); - } - - let ccond = Zenoh060Condition { - condition: imsg::has_flag(self.header, zmsg::flag::K), - codec: self.codec, - }; - let key: WireExpr<'static> = ccond.read(&mut *reader)?; - - let info: QueryableInfo = if imsg::has_flag(self.header, zmsg::flag::Q) { - self.codec.read(&mut *reader)? - } else { - QueryableInfo::default() - }; - - Ok(Queryable { key, info }) - } -} - -// ForgetQueryable -impl WCodec<&ForgetQueryable, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &ForgetQueryable) -> Self::Output { - // Header - let mut header = zmsg::declaration::id::FORGET_QUERYABLE; - if x.key.has_suffix() { - header |= zmsg::flag::K - } - self.write(&mut *writer, header)?; - - // Body - self.write(&mut *writer, &x.key)?; - - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; - codec.read(reader) - } -} - -impl RCodec for Zenoh060Header -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - if imsg::mid(self.header) != zmsg::declaration::id::FORGET_QUERYABLE { - return Err(DidntRead); - } - - let ccond = Zenoh060Condition { - condition: imsg::has_flag(self.header, zmsg::flag::K), - codec: self.codec, - }; - let key: WireExpr<'static> = ccond.read(&mut *reader)?; - - Ok(ForgetQueryable { key }) - } -} diff --git a/commons/zenoh-codec/src/zenoh/del.rs b/commons/zenoh-codec/src/zenoh/del.rs new file mode 100644 index 0000000000..cdd5c332d8 --- /dev/null +++ b/commons/zenoh-codec/src/zenoh/del.rs @@ -0,0 +1,123 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::{common::extension, RCodec, WCodec, Zenoh080, Zenoh080Header}; +use alloc::vec::Vec; +use zenoh_buffers::{ + reader::{DidntRead, Reader}, + writer::{DidntWrite, Writer}, +}; +use zenoh_protocol::{ + common::{iext, imsg}, + zenoh::{ + del::{ext, flag, Del}, + id, + }, +}; + +impl WCodec<&Del, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &Del) -> Self::Output { + // Header + let mut header = id::DEL; + if x.timestamp.is_some() { + header |= flag::T; + } + let mut n_exts = (x.ext_sinfo.is_some()) as u8 + (x.ext_unknown.len() as u8); + if n_exts != 0 { + header |= flag::Z; + } + self.write(&mut *writer, header)?; + + // Body + if let Some(ts) = x.timestamp.as_ref() { + self.write(&mut *writer, ts)?; + } + + // Extensions + if let Some(sinfo) = x.ext_sinfo.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (sinfo, n_exts != 0))?; + } + for u in x.ext_unknown.iter() { + n_exts -= 1; + self.write(&mut *writer, (u, n_exts != 0))?; + } + + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(reader) + } +} + +impl RCodec for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + if imsg::mid(self.header) != id::DEL { + return Err(DidntRead); + } + + // Body + let mut timestamp: Option = None; + if imsg::has_flag(self.header, flag::T) { + timestamp = Some(self.codec.read(&mut *reader)?); + } + + // Extensions + let mut ext_sinfo: Option = None; + let mut ext_unknown = Vec::new(); + + let mut has_ext = imsg::has_flag(self.header, flag::Z); + while has_ext { + let ext: u8 = self.codec.read(&mut *reader)?; + let eodec = Zenoh080Header::new(ext); + match iext::eid(ext) { + ext::SourceInfo::ID => { + let (s, ext): (ext::SourceInfoType, bool) = eodec.read(&mut *reader)?; + ext_sinfo = Some(s); + has_ext = ext; + } + _ => { + let (u, ext) = extension::read(reader, "Del", ext)?; + ext_unknown.push(u); + has_ext = ext; + } + } + } + + Ok(Del { + timestamp, + ext_sinfo, + ext_unknown, + }) + } +} diff --git a/commons/zenoh-codec/src/zenoh/err.rs b/commons/zenoh-codec/src/zenoh/err.rs new file mode 100644 index 0000000000..425044402c --- /dev/null +++ b/commons/zenoh-codec/src/zenoh/err.rs @@ -0,0 +1,144 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::{common::extension, RCodec, WCodec, Zenoh080, Zenoh080Header}; +use alloc::vec::Vec; +use zenoh_buffers::{ + reader::{DidntRead, Reader}, + writer::{DidntWrite, Writer}, +}; +use zenoh_protocol::{ + common::{iext, imsg}, + zenoh::{ + err::{ext, flag, Err}, + id, + }, +}; + +impl WCodec<&Err, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &Err) -> Self::Output { + // Header + let mut header = id::ERR; + if x.timestamp.is_some() { + header |= flag::T; + } + if x.is_infrastructure { + header |= flag::I; + } + let mut n_exts = (x.ext_sinfo.is_some() as u8) + + (x.ext_body.is_some() as u8) + + (x.ext_unknown.len() as u8); + if n_exts != 0 { + header |= flag::Z; + } + self.write(&mut *writer, header)?; + + // Body + self.write(&mut *writer, x.code)?; + if let Some(ts) = x.timestamp.as_ref() { + self.write(&mut *writer, ts)?; + } + + // Extensions + if let Some(sinfo) = x.ext_sinfo.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (sinfo, n_exts != 0))?; + } + if let Some(body) = x.ext_body.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (body, n_exts != 0))?; + } + for u in x.ext_unknown.iter() { + n_exts -= 1; + self.write(&mut *writer, (u, n_exts != 0))?; + } + + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(reader) + } +} + +impl RCodec for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + if imsg::mid(self.header) != id::ERR { + return Err(DidntRead); + } + + // Body + let code: u16 = self.codec.read(&mut *reader)?; + let is_infrastructure = imsg::has_flag(self.header, flag::I); + let mut timestamp: Option = None; + if imsg::has_flag(self.header, flag::T) { + timestamp = Some(self.codec.read(&mut *reader)?); + } + + // Extensions + let mut ext_sinfo: Option = None; + let mut ext_body: Option = None; + let mut ext_unknown = Vec::new(); + + let mut has_ext = imsg::has_flag(self.header, flag::Z); + while has_ext { + let ext: u8 = self.codec.read(&mut *reader)?; + let eodec = Zenoh080Header::new(ext); + match iext::eid(ext) { + ext::SourceInfo::ID => { + let (s, ext): (ext::SourceInfoType, bool) = eodec.read(&mut *reader)?; + ext_sinfo = Some(s); + has_ext = ext; + } + ext::ErrBodyType::VID | ext::ErrBodyType::SID => { + let (s, ext): (ext::ErrBodyType, bool) = eodec.read(&mut *reader)?; + ext_body = Some(s); + has_ext = ext; + } + _ => { + let (u, ext) = extension::read(reader, "Err", ext)?; + ext_unknown.push(u); + has_ext = ext; + } + } + } + + Ok(Err { + code, + is_infrastructure, + timestamp, + ext_sinfo, + ext_body, + ext_unknown, + }) + } +} diff --git a/commons/zenoh-codec/src/zenoh/linkstate.rs b/commons/zenoh-codec/src/zenoh/linkstate.rs deleted file mode 100644 index a51dc1789f..0000000000 --- a/commons/zenoh-codec/src/zenoh/linkstate.rs +++ /dev/null @@ -1,172 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use crate::{RCodec, WCodec, Zenoh060, Zenoh060Header}; -use alloc::vec::Vec; -use zenoh_buffers::{ - reader::{DidntRead, Reader}, - writer::{DidntWrite, Writer}, -}; -use zenoh_protocol::{ - common::imsg, - core::{Locator, WhatAmI, ZInt, ZenohId}, - zenoh::{zmsg, LinkState, LinkStateList}, -}; - -// LinkState -impl WCodec<&LinkState, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &LinkState) -> Self::Output { - // Options - let mut options = 0; - if x.zid.is_some() { - options |= zmsg::link_state::PID; - } - if x.whatami.is_some() { - options |= zmsg::link_state::WAI; - } - if x.locators.is_some() { - options |= zmsg::link_state::LOC; - } - self.write(&mut *writer, options)?; - - // Body - self.write(&mut *writer, &x.psid)?; - self.write(&mut *writer, x.sn)?; - if let Some(zid) = x.zid.as_ref() { - self.write(&mut *writer, zid)?; - } - if let Some(wai) = x.whatami { - let wai: ZInt = wai.into(); - self.write(&mut *writer, wai)?; - } - if let Some(locators) = x.locators.as_ref() { - self.write(&mut *writer, locators.as_slice())?; - } - self.write(&mut *writer, x.links.len())?; - for l in x.links.iter() { - self.write(&mut *writer, *l)?; - } - - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let options: ZInt = self.read(&mut *reader)?; - let psid: ZInt = self.read(&mut *reader)?; - let sn: ZInt = self.read(&mut *reader)?; - let zid = if imsg::has_option(options, zmsg::link_state::PID) { - let zid: ZenohId = self.read(&mut *reader)?; - Some(zid) - } else { - None - }; - let whatami = if imsg::has_option(options, zmsg::link_state::WAI) { - let wai: ZInt = self.read(&mut *reader)?; - Some(WhatAmI::try_from(wai).ok_or(DidntRead)?) - } else { - None - }; - let locators = if imsg::has_option(options, zmsg::link_state::LOC) { - let locs: Vec = self.read(&mut *reader)?; - Some(locs) - } else { - None - }; - let len: usize = self.read(&mut *reader)?; - let mut links: Vec = Vec::with_capacity(len); - for _ in 0..len { - let l: ZInt = self.read(&mut *reader)?; - links.push(l); - } - - Ok(LinkState { - psid, - sn, - zid, - whatami, - locators, - links, - }) - } -} - -// LinkStateList -impl WCodec<&LinkStateList, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &LinkStateList) -> Self::Output { - // Header - let header = zmsg::id::LINK_STATE_LIST; - self.write(&mut *writer, header)?; - - // Body - self.write(&mut *writer, x.link_states.len())?; - for ls in x.link_states.iter() { - self.write(&mut *writer, ls)?; - } - - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; - codec.read(reader) - } -} - -impl RCodec for Zenoh060Header -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - if imsg::mid(self.header) != zmsg::id::LINK_STATE_LIST { - return Err(DidntRead); - } - - let len: usize = self.codec.read(&mut *reader)?; - let mut link_states = Vec::with_capacity(len); - for _ in 0..len { - let ls: LinkState = self.codec.read(&mut *reader)?; - link_states.push(ls); - } - - Ok(LinkStateList { link_states }) - } -} diff --git a/commons/zenoh-codec/src/zenoh/mod.rs b/commons/zenoh-codec/src/zenoh/mod.rs index fc8c66a17c..dea0b7c495 100644 --- a/commons/zenoh-codec/src/zenoh/mod.rs +++ b/commons/zenoh-codec/src/zenoh/mod.rs @@ -1,5 +1,5 @@ // -// Copyright (c) 2023 ZettaScale Technology +// Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at @@ -11,147 +11,359 @@ // Contributors: // ZettaScale Zenoh Team, // -mod data; -mod declare; -mod linkstate; -mod pull; -mod query; -mod routing; -mod unit; - -use crate::{ - RCodec, WCodec, Zenoh060, Zenoh060Header, Zenoh060HeaderReplyContext, Zenoh060Reliability, -}; +pub mod ack; +pub mod del; +pub mod err; +pub mod pull; +pub mod put; +pub mod query; +pub mod reply; + +#[cfg(not(feature = "shared-memory"))] +use crate::Zenoh080Bounded; +#[cfg(feature = "shared-memory")] +use crate::Zenoh080Sliced; +use crate::{LCodec, RCodec, WCodec, Zenoh080, Zenoh080Header, Zenoh080Length}; use zenoh_buffers::{ reader::{DidntRead, Reader}, writer::{DidntWrite, Writer}, + ZBuf, }; +#[cfg(feature = "shared-memory")] +use zenoh_protocol::common::{iext, ZExtUnit}; use zenoh_protocol::{ - common::{imsg, Attachment}, - core::{Channel, Priority, Reliability}, - zenoh::{zmsg, ReplyContext, RoutingContext, ZenohBody, ZenohMessage}, + common::{imsg, ZExtZBufHeader}, + core::{Encoding, ZenohId}, + zenoh::{ext, id, PushBody, RequestBody, ResponseBody}, }; -impl WCodec<&ZenohMessage, &mut W> for Zenoh060 +// Push +impl WCodec<&PushBody, &mut W> for Zenoh080 where W: Writer, { type Output = Result<(), DidntWrite>; - fn write(self, writer: &mut W, x: &ZenohMessage) -> Self::Output { - if let Some(a) = x.attachment.as_ref() { - self.write(&mut *writer, a)?; - } - if let Some(r) = x.routing_context.as_ref() { - self.write(&mut *writer, r)?; - } - if x.channel.priority != Priority::default() { - self.write(&mut *writer, &x.channel.priority)?; + fn write(self, writer: &mut W, x: &PushBody) -> Self::Output { + match x { + PushBody::Put(b) => self.write(&mut *writer, b), + PushBody::Del(b) => self.write(&mut *writer, b), } + } +} - match &x.body { - ZenohBody::Data(d) => self.write(&mut *writer, d), - ZenohBody::Unit(u) => self.write(&mut *writer, u), - ZenohBody::Pull(p) => self.write(&mut *writer, p), - ZenohBody::Query(q) => self.write(&mut *writer, q), - ZenohBody::Declare(d) => self.write(&mut *writer, d), - ZenohBody::LinkStateList(l) => self.write(&mut *writer, l), +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + + let codec = Zenoh080Header::new(header); + let body = match imsg::mid(codec.header) { + id::PUT => PushBody::Put(codec.read(&mut *reader)?), + id::DEL => PushBody::Del(codec.read(&mut *reader)?), + _ => return Err(DidntRead), + }; + + Ok(body) + } +} + +// Request +impl WCodec<&RequestBody, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &RequestBody) -> Self::Output { + match x { + RequestBody::Query(b) => self.write(&mut *writer, b), + RequestBody::Put(b) => self.write(&mut *writer, b), + RequestBody::Del(b) => self.write(&mut *writer, b), + RequestBody::Pull(b) => self.write(&mut *writer, b), } } } -impl RCodec for Zenoh060 +impl RCodec for Zenoh080 where R: Reader, { type Error = DidntRead; - fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Reliability { - reliability: Reliability::default(), - ..Default::default() + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + + let codec = Zenoh080Header::new(header); + let body = match imsg::mid(codec.header) { + id::QUERY => RequestBody::Query(codec.read(&mut *reader)?), + id::PUT => RequestBody::Put(codec.read(&mut *reader)?), + id::DEL => RequestBody::Del(codec.read(&mut *reader)?), + id::PULL => RequestBody::Pull(codec.read(&mut *reader)?), + _ => return Err(DidntRead), }; - codec.read(reader) + + Ok(body) } } -impl RCodec for Zenoh060Reliability +// Response +impl WCodec<&ResponseBody, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &ResponseBody) -> Self::Output { + match x { + ResponseBody::Reply(b) => self.write(&mut *writer, b), + ResponseBody::Err(b) => self.write(&mut *writer, b), + ResponseBody::Ack(b) => self.write(&mut *writer, b), + ResponseBody::Put(b) => self.write(&mut *writer, b), + } + } +} + +impl RCodec for Zenoh080 where R: Reader, { type Error = DidntRead; - fn read(self, reader: &mut R) -> Result { - let mut codec = Zenoh060Header { - header: self.codec.read(&mut *reader)?, - ..Default::default() - }; + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; - let attachment = if imsg::mid(codec.header) == imsg::id::ATTACHMENT { - let a: Attachment = codec.read(&mut *reader)?; - codec.header = self.codec.read(&mut *reader)?; - Some(a) - } else { - None + let codec = Zenoh080Header::new(header); + let body = match imsg::mid(codec.header) { + id::REPLY => ResponseBody::Reply(codec.read(&mut *reader)?), + id::ERR => ResponseBody::Err(codec.read(&mut *reader)?), + id::ACK => ResponseBody::Ack(codec.read(&mut *reader)?), + id::PUT => ResponseBody::Put(codec.read(&mut *reader)?), + _ => return Err(DidntRead), }; - let routing_context = if imsg::mid(codec.header) == zmsg::id::ROUTING_CONTEXT { - let r: RoutingContext = codec.read(&mut *reader)?; - codec.header = self.codec.read(&mut *reader)?; - Some(r) + + Ok(body) + } +} + +// Extension: SourceInfo +impl LCodec<&ext::SourceInfoType<{ ID }>> for Zenoh080 { + fn w_len(self, x: &ext::SourceInfoType<{ ID }>) -> usize { + 1 + self.w_len(&x.zid) + self.w_len(x.eid) + self.w_len(x.sn) + } +} + +impl WCodec<(&ext::SourceInfoType<{ ID }>, bool), &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: (&ext::SourceInfoType<{ ID }>, bool)) -> Self::Output { + let (x, more) = x; + let header: ZExtZBufHeader<{ ID }> = ZExtZBufHeader::new(self.w_len(x)); + self.write(&mut *writer, (&header, more))?; + + let flags: u8 = (x.zid.size() as u8 - 1) << 4; + self.write(&mut *writer, flags)?; + + let lodec = Zenoh080Length::new(x.zid.size()); + lodec.write(&mut *writer, &x.zid)?; + + self.write(&mut *writer, x.eid)?; + self.write(&mut *writer, x.sn)?; + Ok(()) + } +} + +impl RCodec<(ext::SourceInfoType<{ ID }>, bool), &mut R> for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<(ext::SourceInfoType<{ ID }>, bool), Self::Error> { + let (_, more): (ZExtZBufHeader<{ ID }>, bool) = self.read(&mut *reader)?; + + let flags: u8 = self.codec.read(&mut *reader)?; + let length = 1 + ((flags >> 4) as usize); + + let lodec = Zenoh080Length::new(length); + let zid: ZenohId = lodec.read(&mut *reader)?; + + let eid: u32 = self.codec.read(&mut *reader)?; + let sn: u32 = self.codec.read(&mut *reader)?; + + Ok((ext::SourceInfoType { zid, eid, sn }, more)) + } +} + +// Extension: Shm +#[cfg(feature = "shared-memory")] +impl WCodec<(&ext::ShmType<{ ID }>, bool), &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: (&ext::ShmType<{ ID }>, bool)) -> Self::Output { + let (_, more) = x; + let header: ZExtUnit<{ ID }> = ZExtUnit::new(); + self.write(&mut *writer, (&header, more))?; + Ok(()) + } +} + +#[cfg(feature = "shared-memory")] +impl RCodec<(ext::ShmType<{ ID }>, bool), &mut R> for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result<(ext::ShmType<{ ID }>, bool), Self::Error> { + let (_, more): (ZExtUnit<{ ID }>, bool) = self.read(&mut *reader)?; + Ok((ext::ShmType, more)) + } +} + +// Extension ValueType +impl WCodec<(&ext::ValueType<{ VID }, { SID }>, bool), &mut W> + for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: (&ext::ValueType<{ VID }, { SID }>, bool)) -> Self::Output { + let (x, more) = x; + + #[cfg(feature = "shared-memory")] // Write Shm extension if present + if let Some(eshm) = x.ext_shm.as_ref() { + self.write(&mut *writer, (eshm, true))?; + } + + // Compute extension length + let mut len = self.w_len(&x.encoding); + + #[cfg(feature = "shared-memory")] + { + let codec = Zenoh080Sliced::::new(x.ext_shm.is_some()); + len += codec.w_len(&x.payload); + } + + #[cfg(not(feature = "shared-memory"))] + { + let codec = Zenoh080Bounded::::new(); + len += codec.w_len(&x.payload); + } + + // Write ZExtBuf header + let header: ZExtZBufHeader<{ VID }> = ZExtZBufHeader::new(len); + self.write(&mut *writer, (&header, more))?; + + // Write encoding + self.write(&mut *writer, &x.encoding)?; + + // Write payload + fn write(writer: &mut W, payload: &ZBuf) -> Result<(), DidntWrite> + where + W: Writer, + { + // Don't write the length since it is already included in the header + for s in payload.zslices() { + writer.write_zslice(s)?; + } + Ok(()) + } + + #[cfg(feature = "shared-memory")] + { + if x.ext_shm.is_some() { + let codec = Zenoh080Sliced::::new(true); + codec.write(&mut *writer, &x.payload)?; + } else { + write(&mut *writer, &x.payload)?; + } + } + + #[cfg(not(feature = "shared-memory"))] + { + write(&mut *writer, &x.payload)?; + } + + Ok(()) + } +} + +impl RCodec<(ext::ValueType<{ VID }, { SID }>, bool), &mut R> + for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read( + #[allow(unused_mut)] mut self, + reader: &mut R, + ) -> Result<(ext::ValueType<{ VID }, { SID }>, bool), Self::Error> { + #[cfg(feature = "shared-memory")] + let ext_shm = if iext::eid(self.header) == SID { + self.header = self.codec.read(&mut *reader)?; + Some(ext::ShmType) } else { None }; - let priority = if imsg::mid(codec.header) == zmsg::id::PRIORITY { - let p: Priority = codec.read(&mut *reader)?; - codec.header = self.codec.read(&mut *reader)?; - p - } else { - Priority::default() - }; + let (header, more): (ZExtZBufHeader<{ VID }>, bool) = self.read(&mut *reader)?; - let body = match imsg::mid(codec.header) { - zmsg::id::REPLY_CONTEXT => { - let rc: ReplyContext = codec.read(&mut *reader)?; - let rodec = Zenoh060HeaderReplyContext { - header: self.codec.read(&mut *reader)?, - reply_context: Some(rc), - ..Default::default() - }; - match imsg::mid(rodec.header) { - zmsg::id::DATA => ZenohBody::Data(rodec.read(&mut *reader)?), - zmsg::id::UNIT => ZenohBody::Unit(rodec.read(&mut *reader)?), - _ => return Err(DidntRead), + // Read encoding + let start = reader.remaining(); + let encoding: Encoding = self.codec.read(&mut *reader)?; + let end = reader.remaining(); + + // Read payload + fn read(reader: &mut R, len: usize) -> Result + where + R: Reader, + { + let mut payload = ZBuf::empty(); + reader.read_zslices(len, |s| payload.push_zslice(s))?; + Ok(payload) + } + + // Calculate how many bytes are left in the payload + let len = header.len - (start - end); + + let payload: ZBuf = { + #[cfg(feature = "shared-memory")] + { + if ext_shm.is_some() { + let codec = Zenoh080Sliced::::new(true); + let payload: ZBuf = codec.read(&mut *reader)?; + payload + } else { + read(&mut *reader, len)? } } - zmsg::id::DATA => { - let rodec = Zenoh060HeaderReplyContext { - header: codec.header, - ..Default::default() - }; - ZenohBody::Data(rodec.read(&mut *reader)?) - } - zmsg::id::UNIT => { - let rodec = Zenoh060HeaderReplyContext { - header: codec.header, - ..Default::default() - }; - ZenohBody::Unit(rodec.read(&mut *reader)?) + + #[cfg(not(feature = "shared-memory"))] + { + read(&mut *reader, len)? } - zmsg::id::PULL => ZenohBody::Pull(codec.read(&mut *reader)?), - zmsg::id::QUERY => ZenohBody::Query(codec.read(&mut *reader)?), - zmsg::id::DECLARE => ZenohBody::Declare(codec.read(&mut *reader)?), - zmsg::id::LINK_STATE_LIST => ZenohBody::LinkStateList(codec.read(&mut *reader)?), - _ => return Err(DidntRead), }; - Ok(ZenohMessage { - body, - attachment, - channel: Channel { - priority, - reliability: self.reliability, + Ok(( + ext::ValueType { + #[cfg(feature = "shared-memory")] + ext_shm, + encoding, + payload, }, - routing_context, - }) + more, + )) } } diff --git a/commons/zenoh-codec/src/zenoh/pull.rs b/commons/zenoh-codec/src/zenoh/pull.rs index 0421062562..2b2a3a61e0 100644 --- a/commons/zenoh-codec/src/zenoh/pull.rs +++ b/commons/zenoh-codec/src/zenoh/pull.rs @@ -1,5 +1,5 @@ // -// Copyright (c) 2023 ZettaScale Technology +// Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at @@ -11,18 +11,22 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::{RCodec, WCodec, Zenoh060, Zenoh060Condition, Zenoh060Header}; +use crate::{common::extension, RCodec, WCodec, Zenoh080, Zenoh080Header}; +use alloc::vec::Vec; use zenoh_buffers::{ reader::{DidntRead, Reader}, writer::{DidntWrite, Writer}, }; + use zenoh_protocol::{ common::imsg, - core::{WireExpr, ZInt}, - zenoh::{zmsg, Pull}, + zenoh::{ + id, + pull::{flag, Pull}, + }, }; -impl WCodec<&Pull, &mut W> for Zenoh060 +impl WCodec<&Pull, &mut W> for Zenoh080 where W: Writer, { @@ -30,74 +34,58 @@ where fn write(self, writer: &mut W, x: &Pull) -> Self::Output { // Header - let mut header = zmsg::id::PULL; - if x.is_final { - header |= zmsg::flag::F; - } - if x.max_samples.is_some() { - header |= zmsg::flag::N; - } - if x.key.has_suffix() { - header |= zmsg::flag::K; + let mut header = id::PULL; + let mut n_exts = x.ext_unknown.len() as u8; + if n_exts != 0 { + header |= flag::Z; } self.write(&mut *writer, header)?; - // Body - self.write(&mut *writer, &x.key)?; - self.write(&mut *writer, x.pull_id)?; - if let Some(n) = x.max_samples { - self.write(&mut *writer, n)?; + // Extensions + for u in x.ext_unknown.iter() { + n_exts -= 1; + self.write(&mut *writer, (u, n_exts != 0))?; } Ok(()) } } -impl RCodec for Zenoh060 +impl RCodec for Zenoh080 where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); codec.read(reader) } } -impl RCodec for Zenoh060Header +impl RCodec for Zenoh080Header where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - if imsg::mid(self.header) != zmsg::id::PULL { + if imsg::mid(self.header) != id::PULL { return Err(DidntRead); } - let ccond = Zenoh060Condition { - condition: imsg::has_flag(self.header, zmsg::flag::K), - codec: self.codec, - }; - let key: WireExpr<'static> = ccond.read(&mut *reader)?; - let pull_id: ZInt = self.codec.read(&mut *reader)?; - let max_samples = if imsg::has_flag(self.header, zmsg::flag::N) { - let n: ZInt = self.codec.read(&mut *reader)?; - Some(n) - } else { - None - }; - let is_final = imsg::has_flag(self.header, zmsg::flag::F); + // Extensions + let mut ext_unknown = Vec::new(); + + let mut has_ext = imsg::has_flag(self.header, flag::Z); + while has_ext { + let ext: u8 = self.codec.read(&mut *reader)?; + let (u, ext) = extension::read(reader, "Pull", ext)?; + ext_unknown.push(u); + has_ext = ext; + } - Ok(Pull { - key, - pull_id, - max_samples, - is_final, - }) + Ok(Pull { ext_unknown }) } } diff --git a/commons/zenoh-codec/src/zenoh/put.rs b/commons/zenoh-codec/src/zenoh/put.rs new file mode 100644 index 0000000000..6358a533a1 --- /dev/null +++ b/commons/zenoh-codec/src/zenoh/put.rs @@ -0,0 +1,189 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +#[cfg(not(feature = "shared-memory"))] +use crate::Zenoh080Bounded; +#[cfg(feature = "shared-memory")] +use crate::Zenoh080Sliced; +use crate::{common::extension, RCodec, WCodec, Zenoh080, Zenoh080Header}; +use alloc::vec::Vec; +use zenoh_buffers::{ + reader::{DidntRead, Reader}, + writer::{DidntWrite, Writer}, + ZBuf, +}; +use zenoh_protocol::{ + common::{iext, imsg}, + core::Encoding, + zenoh::{ + id, + put::{ext, flag, Put}, + }, +}; + +impl WCodec<&Put, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &Put) -> Self::Output { + // Header + let mut header = id::PUT; + if x.timestamp.is_some() { + header |= flag::T; + } + if x.encoding != Encoding::default() { + header |= flag::E; + } + let mut n_exts = (x.ext_sinfo.is_some()) as u8 + (x.ext_unknown.len() as u8); + #[cfg(feature = "shared-memory")] + { + n_exts += x.ext_shm.is_some() as u8; + } + if n_exts != 0 { + header |= flag::Z; + } + self.write(&mut *writer, header)?; + + // Body + if let Some(ts) = x.timestamp.as_ref() { + self.write(&mut *writer, ts)?; + } + if x.encoding != Encoding::default() { + self.write(&mut *writer, &x.encoding)?; + } + + // Extensions + if let Some(sinfo) = x.ext_sinfo.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (sinfo, n_exts != 0))?; + } + #[cfg(feature = "shared-memory")] + if let Some(eshm) = x.ext_shm.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (eshm, n_exts != 0))?; + } + for u in x.ext_unknown.iter() { + n_exts -= 1; + self.write(&mut *writer, (u, n_exts != 0))?; + } + + // Payload + #[cfg(feature = "shared-memory")] + { + let codec = Zenoh080Sliced::::new(x.ext_shm.is_some()); + codec.write(&mut *writer, &x.payload)?; + } + + #[cfg(not(feature = "shared-memory"))] + { + let bodec = Zenoh080Bounded::::new(); + bodec.write(&mut *writer, &x.payload)?; + } + + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(reader) + } +} + +impl RCodec for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + if imsg::mid(self.header) != id::PUT { + return Err(DidntRead); + } + + // Body + let mut timestamp: Option = None; + if imsg::has_flag(self.header, flag::T) { + timestamp = Some(self.codec.read(&mut *reader)?); + } + + let mut encoding = Encoding::default(); + if imsg::has_flag(self.header, flag::E) { + encoding = self.codec.read(&mut *reader)?; + } + + // Extensions + let mut ext_sinfo: Option = None; + #[cfg(feature = "shared-memory")] + let mut ext_shm: Option = None; + let mut ext_unknown = Vec::new(); + + let mut has_ext = imsg::has_flag(self.header, flag::Z); + while has_ext { + let ext: u8 = self.codec.read(&mut *reader)?; + let eodec = Zenoh080Header::new(ext); + match iext::eid(ext) { + ext::SourceInfo::ID => { + let (s, ext): (ext::SourceInfoType, bool) = eodec.read(&mut *reader)?; + ext_sinfo = Some(s); + has_ext = ext; + } + #[cfg(feature = "shared-memory")] + ext::Shm::ID => { + let (s, ext): (ext::ShmType, bool) = eodec.read(&mut *reader)?; + ext_shm = Some(s); + has_ext = ext; + } + _ => { + let (u, ext) = extension::read(reader, "Put", ext)?; + ext_unknown.push(u); + has_ext = ext; + } + } + } + + // Payload + let payload: ZBuf = { + #[cfg(feature = "shared-memory")] + { + let codec = Zenoh080Sliced::::new(ext_shm.is_some()); + codec.read(&mut *reader)? + } + + #[cfg(not(feature = "shared-memory"))] + { + let bodec = Zenoh080Bounded::::new(); + bodec.read(&mut *reader)? + } + }; + + Ok(Put { + timestamp, + encoding, + ext_sinfo, + #[cfg(feature = "shared-memory")] + ext_shm, + ext_unknown, + payload, + }) + } +} diff --git a/commons/zenoh-codec/src/zenoh/query.rs b/commons/zenoh-codec/src/zenoh/query.rs index 3baf68a998..0844e16df4 100644 --- a/commons/zenoh-codec/src/zenoh/query.rs +++ b/commons/zenoh-codec/src/zenoh/query.rs @@ -1,5 +1,5 @@ // -// Copyright (c) 2023 ZettaScale Technology +// Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at @@ -11,130 +11,63 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::{RCodec, WCodec, Zenoh060, Zenoh060Condition, Zenoh060Header}; -use alloc::string::String; +use crate::{common::extension, RCodec, WCodec, Zenoh080, Zenoh080Header}; +use alloc::{string::String, vec::Vec}; use zenoh_buffers::{ reader::{DidntRead, Reader}, writer::{DidntWrite, Writer}, - ZBuf, }; + use zenoh_protocol::{ - common::imsg, - core::{ConsolidationMode, QueryTarget, WireExpr, ZInt}, - zenoh::{zmsg, DataInfo, Query, QueryBody}, + common::{iext, imsg}, + zenoh::{ + id, + query::{ext, flag, Query}, + }, }; -// QueryTarget -impl WCodec<&QueryTarget, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &QueryTarget) -> Self::Output { - #![allow(clippy::unnecessary_cast)] - match x { - QueryTarget::BestMatching => self.write(&mut *writer, 0 as ZInt)?, - QueryTarget::All => self.write(&mut *writer, 1 as ZInt)?, - QueryTarget::AllComplete => self.write(&mut *writer, 2 as ZInt)?, - #[cfg(feature = "complete_n")] - QueryTarget::Complete(n) => { - self.write(&mut *writer, 3 as ZInt)?; - self.write(&mut *writer, *n)?; - } - } - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let t: ZInt = self.read(&mut *reader)?; - let t = match t { - 0 => QueryTarget::BestMatching, - 1 => QueryTarget::All, - 2 => QueryTarget::AllComplete, - #[cfg(feature = "complete_n")] - 3 => { - let n: ZInt = self.read(&mut *reader)?; - QueryTarget::Complete(n) - } - _ => return Err(DidntRead), - }; - Ok(t) - } -} - -// ConsolidationMode -impl WCodec<&ConsolidationMode, &mut W> for Zenoh060 +// Extension Consolidation +impl WCodec<(ext::ConsolidationType, bool), &mut W> for Zenoh080 where W: Writer, { type Output = Result<(), DidntWrite>; - fn write(self, writer: &mut W, x: &ConsolidationMode) -> Self::Output { - let cm: ZInt = match x { - ConsolidationMode::None => 0, - ConsolidationMode::Monotonic => 1, - ConsolidationMode::Latest => 2, + fn write(self, writer: &mut W, x: (ext::ConsolidationType, bool)) -> Self::Output { + let (x, more) = x; + let v: u64 = match x { + ext::ConsolidationType::Auto => 0, + ext::ConsolidationType::None => 1, + ext::ConsolidationType::Monotonic => 2, + ext::ConsolidationType::Latest => 3, + ext::ConsolidationType::Unique => 4, }; - self.write(&mut *writer, cm)?; - Ok(()) + let v = ext::Consolidation::new(v); + self.write(&mut *writer, (&v, more)) } } -impl RCodec for Zenoh060 +impl RCodec<(ext::ConsolidationType, bool), &mut R> for Zenoh080Header where R: Reader, { type Error = DidntRead; - fn read(self, reader: &mut R) -> Result { - let cm: ZInt = self.read(&mut *reader)?; - let cm = match cm { - 0 => ConsolidationMode::None, - 1 => ConsolidationMode::Monotonic, - 2 => ConsolidationMode::Latest, + fn read(self, reader: &mut R) -> Result<(ext::ConsolidationType, bool), Self::Error> { + let (ext, more): (ext::Consolidation, bool) = self.read(&mut *reader)?; + let c = match ext.value { + 0 => ext::ConsolidationType::Auto, + 1 => ext::ConsolidationType::None, + 2 => ext::ConsolidationType::Monotonic, + 3 => ext::ConsolidationType::Latest, + 4 => ext::ConsolidationType::Unique, _ => return Err(DidntRead), }; - Ok(cm) + Ok((c, more)) } } -// QueryBody -impl WCodec<&QueryBody, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &QueryBody) -> Self::Output { - self.write(&mut *writer, &x.data_info)?; - self.write(&mut *writer, &x.payload)?; - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let data_info: DataInfo = self.read(&mut *reader)?; - let payload: ZBuf = self.read(&mut *reader)?; - Ok(QueryBody { data_info, payload }) - } -} - -// Query -impl WCodec<&Query, &mut W> for Zenoh060 +impl WCodec<&Query, &mut W> for Zenoh080 where W: Writer, { @@ -142,89 +75,116 @@ where fn write(self, writer: &mut W, x: &Query) -> Self::Output { // Header - let mut header = zmsg::id::QUERY; - if x.target.is_some() { - header |= zmsg::flag::T; - } - if x.body.is_some() { - header |= zmsg::flag::B; + let mut header = id::QUERY; + if !x.parameters.is_empty() { + header |= flag::P; } - if x.key.has_suffix() { - header |= zmsg::flag::K; + let mut n_exts = (x.ext_sinfo.is_some() as u8) + + ((x.ext_consolidation != ext::ConsolidationType::default()) as u8) + + (x.ext_body.is_some() as u8) + + (x.ext_unknown.len() as u8); + if n_exts != 0 { + header |= flag::Z; } self.write(&mut *writer, header)?; // Body - self.write(&mut *writer, &x.key)?; - self.write(&mut *writer, x.parameters.as_str())?; - self.write(&mut *writer, x.qid)?; - if let Some(t) = x.target.as_ref() { - self.write(&mut *writer, t)?; + if !x.parameters.is_empty() { + self.write(&mut *writer, &x.parameters)?; } - self.write(&mut *writer, &x.consolidation)?; - if let Some(b) = x.body.as_ref() { - self.write(&mut *writer, b)?; + + // Extensions + if let Some(sinfo) = x.ext_sinfo.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (sinfo, n_exts != 0))?; + } + if x.ext_consolidation != ext::ConsolidationType::default() { + n_exts -= 1; + self.write(&mut *writer, (x.ext_consolidation, n_exts != 0))?; + } + if let Some(body) = x.ext_body.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (body, n_exts != 0))?; + } + for u in x.ext_unknown.iter() { + n_exts -= 1; + self.write(&mut *writer, (u, n_exts != 0))?; } Ok(()) } } -impl RCodec for Zenoh060 +impl RCodec for Zenoh080 where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); codec.read(reader) } } -impl RCodec for Zenoh060Header +impl RCodec for Zenoh080Header where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - if imsg::mid(self.header) != zmsg::id::QUERY { + if imsg::mid(self.header) != id::QUERY { return Err(DidntRead); } - let ccond = Zenoh060Condition { - condition: imsg::has_flag(self.header, zmsg::flag::K), - codec: self.codec, - }; - let key: WireExpr<'static> = ccond.read(&mut *reader)?; - - let parameters: String = self.codec.read(&mut *reader)?; - let qid: ZInt = self.codec.read(&mut *reader)?; - let target = if imsg::has_flag(self.header, zmsg::flag::T) { - let qt: QueryTarget = self.codec.read(&mut *reader)?; - Some(qt) - } else { - None - }; - let consolidation: ConsolidationMode = self.codec.read(&mut *reader)?; - let body = if imsg::has_flag(self.header, zmsg::flag::B) { - let qb: QueryBody = self.codec.read(&mut *reader)?; - Some(qb) - } else { - None - }; + // Body + let mut parameters = String::new(); + if imsg::has_flag(self.header, flag::P) { + parameters = self.codec.read(&mut *reader)?; + } + + // Extensions + let mut ext_sinfo: Option = None; + let mut ext_consolidation = ext::ConsolidationType::default(); + let mut ext_body: Option = None; + let mut ext_unknown = Vec::new(); + + let mut has_ext = imsg::has_flag(self.header, flag::Z); + while has_ext { + let ext: u8 = self.codec.read(&mut *reader)?; + let eodec = Zenoh080Header::new(ext); + match iext::eid(ext) { + ext::SourceInfo::ID => { + let (s, ext): (ext::SourceInfoType, bool) = eodec.read(&mut *reader)?; + ext_sinfo = Some(s); + has_ext = ext; + } + ext::Consolidation::ID => { + let (c, ext): (ext::ConsolidationType, bool) = eodec.read(&mut *reader)?; + ext_consolidation = c; + has_ext = ext; + } + ext::QueryBodyType::SID | ext::QueryBodyType::VID => { + let (s, ext): (ext::QueryBodyType, bool) = eodec.read(&mut *reader)?; + ext_body = Some(s); + has_ext = ext; + } + _ => { + let (u, ext) = extension::read(reader, "Query", ext)?; + ext_unknown.push(u); + has_ext = ext; + } + } + } Ok(Query { - key, parameters, - qid, - target, - consolidation, - body, + ext_sinfo, + ext_consolidation, + ext_body, + ext_unknown, }) } } diff --git a/commons/zenoh-codec/src/zenoh/reply.rs b/commons/zenoh-codec/src/zenoh/reply.rs new file mode 100644 index 0000000000..1aef954220 --- /dev/null +++ b/commons/zenoh-codec/src/zenoh/reply.rs @@ -0,0 +1,202 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +#[cfg(not(feature = "shared-memory"))] +use crate::Zenoh080Bounded; +#[cfg(feature = "shared-memory")] +use crate::Zenoh080Sliced; +use crate::{common::extension, RCodec, WCodec, Zenoh080, Zenoh080Header}; +use alloc::vec::Vec; +use zenoh_buffers::{ + reader::{DidntRead, Reader}, + writer::{DidntWrite, Writer}, + ZBuf, +}; +use zenoh_protocol::{ + common::{iext, imsg}, + core::Encoding, + zenoh::{ + id, + reply::{ext, flag, Reply}, + }, +}; + +impl WCodec<&Reply, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &Reply) -> Self::Output { + // Header + let mut header = id::REPLY; + if x.timestamp.is_some() { + header |= flag::T; + } + if x.encoding != Encoding::default() { + header |= flag::E; + } + let mut n_exts = (x.ext_sinfo.is_some()) as u8 + + ((x.ext_consolidation != ext::ConsolidationType::default()) as u8) + + (x.ext_unknown.len() as u8); + #[cfg(feature = "shared-memory")] + { + n_exts += x.ext_shm.is_some() as u8; + } + if n_exts != 0 { + header |= flag::Z; + } + self.write(&mut *writer, header)?; + + // Body + if let Some(ts) = x.timestamp.as_ref() { + self.write(&mut *writer, ts)?; + } + if x.encoding != Encoding::default() { + self.write(&mut *writer, &x.encoding)?; + } + + // Extensions + if let Some(sinfo) = x.ext_sinfo.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (sinfo, n_exts != 0))?; + } + if x.ext_consolidation != ext::ConsolidationType::default() { + n_exts -= 1; + self.write(&mut *writer, (x.ext_consolidation, n_exts != 0))?; + } + #[cfg(feature = "shared-memory")] + if let Some(eshm) = x.ext_shm.as_ref() { + n_exts -= 1; + self.write(&mut *writer, (eshm, n_exts != 0))?; + } + for u in x.ext_unknown.iter() { + n_exts -= 1; + self.write(&mut *writer, (u, n_exts != 0))?; + } + + // Payload + #[cfg(feature = "shared-memory")] + { + let codec = Zenoh080Sliced::::new(x.ext_shm.is_some()); + codec.write(&mut *writer, &x.payload)?; + } + + #[cfg(not(feature = "shared-memory"))] + { + let bodec = Zenoh080Bounded::::new(); + bodec.write(&mut *writer, &x.payload)?; + } + + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let header: u8 = self.read(&mut *reader)?; + let codec = Zenoh080Header::new(header); + codec.read(reader) + } +} + +impl RCodec for Zenoh080Header +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + if imsg::mid(self.header) != id::REPLY { + return Err(DidntRead); + } + + // Body + let mut timestamp: Option = None; + if imsg::has_flag(self.header, flag::T) { + timestamp = Some(self.codec.read(&mut *reader)?); + } + + let mut encoding = Encoding::default(); + if imsg::has_flag(self.header, flag::E) { + encoding = self.codec.read(&mut *reader)?; + } + + // Extensions + let mut ext_sinfo: Option = None; + let mut ext_consolidation = ext::ConsolidationType::default(); + #[cfg(feature = "shared-memory")] + let mut ext_shm: Option = None; + let mut ext_unknown = Vec::new(); + + let mut has_ext = imsg::has_flag(self.header, flag::Z); + while has_ext { + let ext: u8 = self.codec.read(&mut *reader)?; + let eodec = Zenoh080Header::new(ext); + match iext::eid(ext) { + ext::SourceInfo::ID => { + let (s, ext): (ext::SourceInfoType, bool) = eodec.read(&mut *reader)?; + ext_sinfo = Some(s); + has_ext = ext; + } + ext::Consolidation::ID => { + let (c, ext): (ext::ConsolidationType, bool) = eodec.read(&mut *reader)?; + ext_consolidation = c; + has_ext = ext; + } + #[cfg(feature = "shared-memory")] + ext::Shm::ID => { + let (s, ext): (ext::ShmType, bool) = eodec.read(&mut *reader)?; + ext_shm = Some(s); + has_ext = ext; + } + _ => { + let (u, ext) = extension::read(reader, "Reply", ext)?; + ext_unknown.push(u); + has_ext = ext; + } + } + } + + // Payload + let payload: ZBuf = { + #[cfg(feature = "shared-memory")] + { + let codec = Zenoh080Sliced::::new(ext_shm.is_some()); + codec.read(&mut *reader)? + } + + #[cfg(not(feature = "shared-memory"))] + { + let bodec = Zenoh080Bounded::::new(); + bodec.read(&mut *reader)? + } + }; + + Ok(Reply { + timestamp, + encoding, + ext_sinfo, + ext_consolidation, + #[cfg(feature = "shared-memory")] + ext_shm, + ext_unknown, + payload, + }) + } +} diff --git a/commons/zenoh-codec/src/zenoh/routing.rs b/commons/zenoh-codec/src/zenoh/routing.rs deleted file mode 100644 index fa11d06283..0000000000 --- a/commons/zenoh-codec/src/zenoh/routing.rs +++ /dev/null @@ -1,71 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use crate::{RCodec, WCodec, Zenoh060, Zenoh060Header}; -use zenoh_buffers::{ - reader::{DidntRead, Reader}, - writer::{DidntWrite, Writer}, -}; -use zenoh_protocol::{ - common::imsg, - core::ZInt, - zenoh::{zmsg, RoutingContext}, -}; - -impl WCodec<&RoutingContext, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &RoutingContext) -> Self::Output { - // Header - let header = zmsg::id::ROUTING_CONTEXT; - self.write(&mut *writer, header)?; - - // Body - self.write(&mut *writer, x.tree_id)?; - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let codec = Zenoh060Header { - header: self.read(&mut *reader)?, - ..Default::default() - }; - codec.read(reader) - } -} - -impl RCodec for Zenoh060Header -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - if imsg::mid(self.header) != zmsg::id::ROUTING_CONTEXT { - return Err(DidntRead); - } - - let tree_id: ZInt = self.codec.read(&mut *reader)?; - Ok(RoutingContext { tree_id }) - } -} diff --git a/commons/zenoh-codec/src/zenoh/unit.rs b/commons/zenoh-codec/src/zenoh/unit.rs deleted file mode 100644 index 388c214273..0000000000 --- a/commons/zenoh-codec/src/zenoh/unit.rs +++ /dev/null @@ -1,92 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use crate::{RCodec, WCodec, Zenoh060, Zenoh060Header, Zenoh060HeaderReplyContext}; -use zenoh_buffers::{ - reader::{DidntRead, Reader}, - writer::{DidntWrite, Writer}, -}; -use zenoh_protocol::{ - common::imsg, - core::CongestionControl, - zenoh::{zmsg, Unit}, -}; - -impl WCodec<&Unit, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &Unit) -> Self::Output { - if let Some(reply_context) = x.reply_context.as_ref() { - self.write(&mut *writer, reply_context)?; - } - - // Header - let mut header = zmsg::id::UNIT; - if x.congestion_control == CongestionControl::Drop { - header |= zmsg::flag::D; - } - self.write(&mut *writer, header)?; - - // Body - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let mut codec = Zenoh060HeaderReplyContext { - header: self.read(&mut *reader)?, - ..Default::default() - }; - if imsg::mid(codec.header) == zmsg::id::REPLY_CONTEXT { - let hodec = Zenoh060Header { - header: codec.header, - ..Default::default() - }; - codec.reply_context = Some(hodec.read(&mut *reader)?); - codec.header = self.read(&mut *reader)?; - } - codec.read(reader) - } -} - -impl RCodec for Zenoh060HeaderReplyContext -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, _reader: &mut R) -> Result { - if imsg::mid(self.header) != zmsg::id::UNIT { - return Err(DidntRead); - } - - let congestion_control = if imsg::has_flag(self.header, zmsg::flag::D) { - CongestionControl::Drop - } else { - CongestionControl::Block - }; - Ok(Unit { - congestion_control, - reply_context: self.reply_context, - }) - } -} diff --git a/commons/zenoh-codec/tests/codec.rs b/commons/zenoh-codec/tests/codec.rs index c07fec7c1f..3fdb95e1b5 100644 --- a/commons/zenoh-codec/tests/codec.rs +++ b/commons/zenoh-codec/tests/codec.rs @@ -16,14 +16,20 @@ use rand::{ *, }; use std::convert::TryFrom; -use std::sync::Arc; use zenoh_buffers::{ reader::{HasReader, Reader}, writer::HasWriter, BBuf, ZBuf, ZSlice, }; use zenoh_codec::*; -use zenoh_protocol::{common::*, core::*, scouting::*, transport::*, zenoh::*}; +use zenoh_protocol::{ + common::*, + core::*, + network::{self, *}, + scouting::*, + transport::{self, *}, + zenoh, zextunit, zextz64, zextzbuf, +}; const NUM_ITER: usize = 100; const MAX_PAYLOAD_SIZE: usize = 256; @@ -39,9 +45,8 @@ macro_rules! run_single { let mut reader = $buff.reader(); let y: $type = $rcode.read(&mut reader).unwrap(); - assert!(!reader.can_read()); - assert_eq!(x, y); + assert!(!reader.can_read()); } }; } @@ -55,7 +60,7 @@ macro_rules! run_fragmented { let mut writer = vbuf.writer(); $wcode.write(&mut writer, &x).unwrap(); - let mut zbuf = ZBuf::default(); + let mut zbuf = ZBuf::empty(); let mut reader = vbuf.reader(); while let Ok(b) = reader.read_u8() { zbuf.push_zslice(vec![b].into()); @@ -63,9 +68,8 @@ macro_rules! run_fragmented { let mut reader = zbuf.reader(); let y: $type = $rcode.read(&mut reader).unwrap(); - assert!(!reader.can_read()); - assert_eq!(x, y); + assert!(!reader.can_read()); } }; } @@ -81,7 +85,7 @@ macro_rules! run_buffers { run_single!($type, $rand, $wcode, $rcode, buffer); println!("ZBuf: codec {}", std::any::type_name::<$type>()); - let mut buffer = ZBuf::default(); + let mut buffer = ZBuf::empty(); run_single!($type, $rand, $wcode, $rcode, buffer); println!("ZSlice: codec {}", std::any::type_name::<$type>()); @@ -92,12 +96,11 @@ macro_rules! run_buffers { let mut writer = buffer.writer(); $wcode.write(&mut writer, &x).unwrap(); - let mut zslice = ZSlice::from(Arc::new(buffer)); + let mut zslice = ZSlice::from(buffer); let mut reader = zslice.reader(); let y: $type = $rcode.read(&mut reader).unwrap(); - assert!(!reader.can_read()); - assert_eq!(x, y); + assert!(!reader.can_read()); } println!("Fragmented: codec {}", std::any::type_name::<$type>()); @@ -107,7 +110,7 @@ macro_rules! run_buffers { macro_rules! run { ($type:ty, $rand:expr) => { - let codec = Zenoh060::default(); + let codec = Zenoh080::new(); run_buffers!($type, $rand, codec, codec); }; ($type:ty, $rand:expr, $wcode:block, $rcode:block) => { @@ -118,7 +121,83 @@ macro_rules! run { // Core #[test] fn codec_zint() { - run!(ZInt, thread_rng().gen::()); + run!(u8, { thread_rng().gen::() }); + run!(u16, { thread_rng().gen::() }); + run!(u32, { thread_rng().gen::() }); + run!(u64, { thread_rng().gen::() }); + run!(usize, thread_rng().gen::()); +} + +#[test] +fn codec_zint_len() { + let codec = Zenoh080::new(); + + let mut buff = vec![]; + let mut writer = buff.writer(); + let n: u64 = 0; + codec.write(&mut writer, n).unwrap(); + assert_eq!(codec.w_len(n), buff.len()); + + for i in 1..=9 { + let mut buff = vec![]; + let mut writer = buff.writer(); + let n: u64 = 1 << (7 * i); + codec.write(&mut writer, n).unwrap(); + assert_eq!(codec.w_len(n), buff.len()); + } + + let mut buff = vec![]; + let mut writer = buff.writer(); + let n = u64::MAX; + codec.write(&mut writer, n).unwrap(); + assert_eq!(codec.w_len(n), buff.len()); +} + +#[test] +fn codec_zint_bounded() { + use crate::Zenoh080Bounded; + + // Check bounded encoding + for i in [ + u8::MAX as u64, + u16::MAX as u64, + u32::MAX as u64, + usize::MAX as u64, + u64::MAX, + ] { + let mut buff = vec![]; + + let codec = Zenoh080::new(); + let mut writer = buff.writer(); + codec.write(&mut writer, i).unwrap(); + + macro_rules! check { + ($zint:ty) => { + println!("Bound: {}. Value: {}", std::any::type_name::<$zint>(), i); + let zodec = Zenoh080Bounded::<$zint>::new(); + + let mut btmp = vec![]; + let mut wtmp = btmp.writer(); + let w_res = zodec.write(&mut wtmp, i); + + let mut reader = buff.reader(); + let r_res: Result<$zint, _> = zodec.read(&mut reader); + if i <= <$zint>::MAX as u64 { + w_res.unwrap(); + assert_eq!(i, r_res.unwrap() as u64); + } else { + assert!(w_res.is_err()); + assert!(r_res.is_err()); + } + }; + } + + check!(u8); + check!(u16); + check!(u32); + check!(u64); + check!(usize); + } } #[test] @@ -130,11 +209,68 @@ fn codec_string() { }); } +#[test] +fn codec_string_bounded() { + use crate::Zenoh080Bounded; + + let mut rng = rand::thread_rng(); + let str = Alphanumeric.sample_string(&mut rng, 1 + u8::MAX as usize); + + let zodec = Zenoh080Bounded::::new(); + let codec = Zenoh080::new(); + + let mut buff = vec![]; + + let mut writer = buff.writer(); + assert!(zodec.write(&mut writer, &str).is_err()); + let mut writer = buff.writer(); + codec.write(&mut writer, &str).unwrap(); + + let mut reader = buff.reader(); + let r_res: Result = zodec.read(&mut reader); + assert!(r_res.is_err()); + let mut reader = buff.reader(); + let r_res: Result = codec.read(&mut reader); + assert_eq!(str, r_res.unwrap()); +} + #[test] fn codec_zid() { run!(ZenohId, ZenohId::default()); } +#[test] +fn codec_zslice() { + run!( + ZSlice, + ZSlice::rand(thread_rng().gen_range(1..=MAX_PAYLOAD_SIZE)) + ); +} + +#[test] +fn codec_zslice_bounded() { + use crate::Zenoh080Bounded; + + let zslice = ZSlice::rand(1 + u8::MAX as usize); + + let zodec = Zenoh080Bounded::::new(); + let codec = Zenoh080::new(); + + let mut buff = vec![]; + + let mut writer = buff.writer(); + assert!(zodec.write(&mut writer, &zslice).is_err()); + let mut writer = buff.writer(); + codec.write(&mut writer, &zslice).unwrap(); + + let mut reader = buff.reader(); + let r_res: Result = zodec.read(&mut reader); + assert!(r_res.is_err()); + let mut reader = buff.reader(); + let r_res: Result = codec.read(&mut reader); + assert_eq!(zslice, r_res.unwrap()); +} + #[test] fn codec_zbuf() { run!( @@ -144,8 +280,27 @@ fn codec_zbuf() { } #[test] -fn codec_endpoint() { - run!(EndPoint, EndPoint::rand()); +fn codec_zbuf_bounded() { + use crate::Zenoh080Bounded; + + let zbuf = ZBuf::rand(1 + u8::MAX as usize); + + let zodec = Zenoh080Bounded::::new(); + let codec = Zenoh080::new(); + + let mut buff = vec![]; + + let mut writer = buff.writer(); + assert!(zodec.write(&mut writer, &zbuf).is_err()); + let mut writer = buff.writer(); + codec.write(&mut writer, &zbuf).unwrap(); + + let mut reader = buff.reader(); + let r_res: Result = zodec.read(&mut reader); + assert!(r_res.is_err()); + let mut reader = buff.reader(); + let r_res: Result = codec.read(&mut reader); + assert_eq!(zbuf, r_res.unwrap()); } #[test] @@ -167,23 +322,88 @@ fn codec_encoding() { run!(Encoding, Encoding::rand()); } -// Common +#[cfg(feature = "shared-memory")] #[test] -fn codec_attachment() { - run!(Attachment, Attachment::rand()); +fn codec_shm_info() { + use zenoh_shm::SharedMemoryBufInfo; + + run!(SharedMemoryBufInfo, { + let mut rng = rand::thread_rng(); + let len = rng.gen_range(0..16); + SharedMemoryBufInfo::new( + rng.gen(), + rng.gen(), + Alphanumeric.sample_string(&mut rng, len), + rng.gen(), + ) + }); } -// Scouting +// Common #[test] -fn codec_hello() { - run!(Hello, Hello::rand()); +fn codec_extension() { + let _ = env_logger::try_init(); + + macro_rules! run_extension_single { + ($ext:ty, $buff:expr) => { + let codec = Zenoh080::new(); + for _ in 0..NUM_ITER { + let more: bool = thread_rng().gen(); + let x: (&$ext, bool) = (&<$ext>::rand(), more); + + $buff.clear(); + let mut writer = $buff.writer(); + codec.write(&mut writer, x).unwrap(); + + let mut reader = $buff.reader(); + let y: ($ext, bool) = codec.read(&mut reader).unwrap(); + assert!(!reader.can_read()); + + assert_eq!(x.0, &y.0); + assert_eq!(x.1, y.1); + + let mut reader = $buff.reader(); + let _ = zenoh_codec::common::extension::skip_all(&mut reader, "Test"); + } + }; + } + + macro_rules! run_extension { + ($type:ty) => { + println!("Vec: codec {}", std::any::type_name::<$type>()); + let mut buff = vec![]; + run_extension_single!($type, buff); + + println!("BBuf: codec {}", std::any::type_name::<$type>()); + let mut buff = BBuf::with_capacity(u16::MAX as usize); + run_extension_single!($type, buff); + + println!("ZBuf: codec {}", std::any::type_name::<$type>()); + let mut buff = ZBuf::empty(); + run_extension_single!($type, buff); + }; + } + + run_extension!(zextunit!(0x00, true)); + run_extension!(zextunit!(0x00, false)); + run_extension!(zextz64!(0x01, true)); + run_extension!(zextz64!(0x01, false)); + run_extension!(zextzbuf!(0x02, true)); + run_extension!(zextzbuf!(0x02, false)); + run_extension!(ZExtUnknown); } +// Scouting #[test] fn codec_scout() { run!(Scout, Scout::rand()); } +#[test] +fn codec_hello() { + run!(Hello, Hello::rand()); +} + #[test] fn codec_scouting() { run!(ScoutingMessage, ScoutingMessage::rand()); @@ -235,127 +455,139 @@ fn codec_frame() { run!(Frame, Frame::rand()); } +#[test] +fn codec_fragment_header() { + run!(FragmentHeader, FragmentHeader::rand()); +} + +#[test] +fn codec_fragment() { + run!(Fragment, Fragment::rand()); +} + +#[test] +fn codec_transport_oam() { + run!(transport::Oam, transport::Oam::rand()); +} + #[test] fn codec_transport() { run!(TransportMessage, TransportMessage::rand()); } -// Zenoh +// Network #[test] -fn codec_routing_context() { - run!(RoutingContext, RoutingContext::rand()); +fn codec_declare() { + run!(Declare, Declare::rand()); } #[test] -fn codec_reply_context() { - run!(ReplyContext, ReplyContext::rand()); +fn codec_declare_body() { + run!(DeclareBody, DeclareBody::rand()); } #[test] -fn codec_data_info() { - run!(DataInfo, DataInfo::rand()); +fn codec_declare_keyexpr() { + run!(DeclareKeyExpr, DeclareKeyExpr::rand()); } #[test] -fn codec_data() { - run!(Data, Data::rand()); +fn codec_undeclare_keyexpr() { + run!(UndeclareKeyExpr, UndeclareKeyExpr::rand()); } #[test] -fn codec_unit() { - run!(Unit, Unit::rand()); +fn codec_declare_subscriber() { + run!(DeclareSubscriber, DeclareSubscriber::rand()); } #[test] -fn codec_pull() { - run!(Pull, Pull::rand()); +fn codec_undeclare_subscriber() { + run!(UndeclareSubscriber, UndeclareSubscriber::rand()); } #[test] -fn codec_query() { - run!(Query, Query::rand()); +fn codec_declare_queryable() { + run!(DeclareQueryable, DeclareQueryable::rand()); } #[test] -fn codec_declaration_resource() { - run!(Resource, Resource::rand()); +fn codec_undeclare_queryable() { + run!(UndeclareQueryable, UndeclareQueryable::rand()); } #[test] -fn codec_declaration_forget_resource() { - run!(ForgetResource, ForgetResource::rand()); +fn codec_declare_token() { + run!(DeclareToken, DeclareToken::rand()); } #[test] -fn codec_declaration_publisher() { - run!(Publisher, Publisher::rand()); +fn codec_undeclare_token() { + run!(UndeclareToken, UndeclareToken::rand()); } #[test] -fn codec_declaration_forget_publisher() { - run!(ForgetPublisher, ForgetPublisher::rand()); +fn codec_push() { + run!(Push, Push::rand()); } #[test] -fn codec_declaration_subscriber() { - run!(Subscriber, Subscriber::rand()); +fn codec_request() { + run!(Request, Request::rand()); } #[test] -fn codec_declaration_forget_subscriber() { - run!(ForgetSubscriber, ForgetSubscriber::rand()); +fn codec_response() { + run!(Response, Response::rand()); } #[test] -fn codec_declaration_queryable() { - run!(Queryable, Queryable::rand()); +fn codec_response_final() { + run!(ResponseFinal, ResponseFinal::rand()); } #[test] -fn codec_declaration_forget_queryable() { - run!(ForgetQueryable, ForgetQueryable::rand()); +fn codec_network_oam() { + run!(network::Oam, network::Oam::rand()); } #[test] -fn codec_declaration() { - run!(Declaration, Declaration::rand()); +fn codec_network() { + run!(NetworkMessage, NetworkMessage::rand()); } +// Zenoh new #[test] -fn codec_declare() { - run!(Declare, Declare::rand()); +fn codec_put() { + run!(zenoh::Put, zenoh::Put::rand()); } #[test] -fn codec_link_state() { - run!(LinkState, LinkState::rand()); +fn codec_del() { + run!(zenoh::Del, zenoh::Del::rand()); } #[test] -fn codec_link_state_list() { - run!(LinkStateList, LinkStateList::rand()); +fn codec_query() { + run!(zenoh::Query, zenoh::Query::rand()); } #[test] -fn codec_zenoh() { - run!( - ZenohMessage, - { - let mut x = ZenohMessage::rand(); - x.channel.reliability = Reliability::Reliable; - x - }, - { Zenoh060::default() }, - { Zenoh060Reliability::new(Reliability::Reliable) } - ); - run!( - ZenohMessage, - { - let mut x = ZenohMessage::rand(); - x.channel.reliability = Reliability::BestEffort; - x - }, - { Zenoh060::default() }, - { Zenoh060Reliability::new(Reliability::BestEffort) } - ); +fn codec_reply() { + run!(zenoh::Reply, zenoh::Reply::rand()); +} + +#[test] +fn codec_err() { + run!(zenoh::Err, zenoh::Err::rand()); +} + +#[test] +fn codec_ack() { + run!(zenoh::Ack, zenoh::Ack::rand()); +} + +#[test] +fn codec_pull() { + run!(zenoh::Pull, zenoh::Pull::rand()); } diff --git a/commons/zenoh-collections/src/lib.rs b/commons/zenoh-collections/src/lib.rs index 6549594de2..ea9a9209e6 100644 --- a/commons/zenoh-collections/src/lib.rs +++ b/commons/zenoh-collections/src/lib.rs @@ -32,3 +32,8 @@ pub use ring_buffer::*; pub mod stack_buffer; #[cfg(feature = "std")] pub use stack_buffer::*; + +#[cfg(feature = "std")] +pub mod properties; +#[cfg(feature = "std")] +pub use properties::*; diff --git a/commons/zenoh-collections/src/properties.rs b/commons/zenoh-collections/src/properties.rs new file mode 100644 index 0000000000..6da7178c57 --- /dev/null +++ b/commons/zenoh-collections/src/properties.rs @@ -0,0 +1,175 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use std::{ + collections::HashMap, + convert::{From, TryFrom}, + fmt, + ops::{Deref, DerefMut}, +}; + +const PROP_SEPS: &[&str] = &["\r\n", "\n", ";"]; +const DEFAULT_PROP_SEP: char = ';'; +const KV_SEP: char = '='; +const COMMENT_PREFIX: char = '#'; + +/// A map of key/value (String,String) properties. +/// It can be parsed from a String, using `;` or `` as separator between each properties +/// and `=` as separator between a key and its value. Keys and values are trimed. +#[non_exhaustive] +#[derive(Clone, PartialEq, Eq, Default)] +pub struct Properties(HashMap); + +impl Deref for Properties { + type Target = HashMap; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Properties { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl fmt::Display for Properties { + /// Format the Properties as a string, using `'='` for key/value separator + /// and `';'` for separator between each keys/values. + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut it = self.0.iter(); + if let Some((k, v)) = it.next() { + if v.is_empty() { + write!(f, "{k}")? + } else { + write!(f, "{k}{KV_SEP}{v}")? + } + for (k, v) in it { + if v.is_empty() { + write!(f, "{DEFAULT_PROP_SEP}{k}")? + } else { + write!(f, "{DEFAULT_PROP_SEP}{k}{KV_SEP}{v}")? + } + } + } + Ok(()) + } +} + +impl fmt::Debug for Properties { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{self}") + } +} + +impl From<&str> for Properties { + fn from(s: &str) -> Self { + let mut props = vec![s]; + for sep in PROP_SEPS { + props = props + .into_iter() + .flat_map(|s| s.split(sep)) + .collect::>(); + } + props = props.into_iter().map(str::trim).collect::>(); + let inner = props + .iter() + .filter_map(|prop| { + if prop.is_empty() || prop.starts_with(COMMENT_PREFIX) { + None + } else { + let mut it = prop.splitn(2, KV_SEP); + Some(( + it.next().unwrap().trim().to_string(), + it.next().unwrap_or("").trim().to_string(), + )) + } + }) + .collect(); + Self(inner) + } +} + +impl From for Properties { + fn from(s: String) -> Self { + Self::from(s.as_str()) + } +} + +impl From> for Properties { + fn from(map: HashMap) -> Self { + Self(map) + } +} + +impl From<&[(&str, &str)]> for Properties { + fn from(kvs: &[(&str, &str)]) -> Self { + let inner = kvs + .iter() + .map(|(k, v)| ((*k).to_string(), (*v).to_string())) + .collect(); + Self(inner) + } +} + +impl TryFrom<&std::path::Path> for Properties { + type Error = std::io::Error; + + fn try_from(p: &std::path::Path) -> Result { + Ok(Self::from(std::fs::read_to_string(p)?)) + } +} + +impl From for HashMap { + fn from(props: Properties) -> Self { + props.0 + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_properties() { + assert!(Properties::from("").0.is_empty()); + + assert_eq!(Properties::from("p1"), Properties::from(&[("p1", "")][..])); + + assert_eq!( + Properties::from("p1=v1"), + Properties::from(&[("p1", "v1")][..]) + ); + + assert_eq!( + Properties::from("p1=v1;p2=v2;"), + Properties::from(&[("p1", "v1"), ("p2", "v2")][..]) + ); + + assert_eq!( + Properties::from("p1=v1;p2;p3=v3"), + Properties::from(&[("p1", "v1"), ("p2", ""), ("p3", "v3")][..]) + ); + + assert_eq!( + Properties::from("p1=v 1;p 2=v2"), + Properties::from(&[("p1", "v 1"), ("p 2", "v2")][..]) + ); + + assert_eq!( + Properties::from("p1=x=y;p2=a==b"), + Properties::from(&[("p1", "x=y"), ("p2", "a==b")][..]) + ); + } +} diff --git a/commons/zenoh-collections/src/ring_buffer.rs b/commons/zenoh-collections/src/ring_buffer.rs index 6a559dd3e1..fd60030ebc 100644 --- a/commons/zenoh-collections/src/ring_buffer.rs +++ b/commons/zenoh-collections/src/ring_buffer.rs @@ -20,6 +20,7 @@ pub struct RingBuffer { } impl RingBuffer { + #[must_use] pub fn new(capacity: usize) -> RingBuffer { let buffer = VecDeque::::with_capacity(capacity); RingBuffer { @@ -50,21 +51,25 @@ impl RingBuffer { #[allow(dead_code)] #[inline] + #[must_use] pub fn is_empty(&self) -> bool { self.buffer.is_empty() } #[inline] + #[must_use] pub fn is_full(&self) -> bool { self.len() == self.capacity() } #[inline] + #[must_use] pub fn len(&self) -> usize { self.len } #[inline] + #[must_use] pub fn capacity(&self) -> usize { self.capacity } diff --git a/commons/zenoh-collections/src/single_or_vec.rs b/commons/zenoh-collections/src/single_or_vec.rs index 558e2439bf..ea190395fb 100644 --- a/commons/zenoh-collections/src/single_or_vec.rs +++ b/commons/zenoh-collections/src/single_or_vec.rs @@ -31,7 +31,7 @@ impl SingleOrVecInner { SingleOrVecInner::Vec(vec) if vec.capacity() == 0 => *self = Self::Single(value), SingleOrVecInner::Single(first) => unsafe { let first = ptr::read(first); - ptr::write(self, Self::Vec(vec![first, value])) + ptr::write(self, Self::Vec(vec![first, value])); }, SingleOrVecInner::Vec(vec) => vec.push(value), } @@ -85,14 +85,14 @@ pub struct SingleOrVec(SingleOrVecInner); impl SingleOrVec { pub fn push(&mut self, value: T) { - self.0.push(value) + self.0.push(value); } pub fn truncate(&mut self, len: usize) { if let SingleOrVecInner::Vec(v) = &mut self.0 { - v.truncate(len) + v.truncate(len); } else if len == 0 { - self.0 = SingleOrVecInner::Vec(Vec::new()) + self.0 = SingleOrVecInner::Vec(Vec::new()); } } @@ -143,7 +143,7 @@ impl SingleOrVec { impl Default for SingleOrVec { fn default() -> Self { - Self(Default::default()) + Self(SingleOrVecInner::default()) } } @@ -192,7 +192,7 @@ impl IntoIterator for SingleOrVec { impl iter::Extend for SingleOrVec { fn extend>(&mut self, iter: I) { for value in iter { - self.push(value) + self.push(value); } } } diff --git a/commons/zenoh-collections/src/stack_buffer.rs b/commons/zenoh-collections/src/stack_buffer.rs index 893e40d401..dd3ae11d62 100644 --- a/commons/zenoh-collections/src/stack_buffer.rs +++ b/commons/zenoh-collections/src/stack_buffer.rs @@ -18,6 +18,7 @@ pub struct StackBuffer { } impl StackBuffer { + #[must_use] pub fn new(capacity: usize) -> StackBuffer { let buffer = VecDeque::::with_capacity(capacity); StackBuffer { buffer } @@ -40,21 +41,25 @@ impl StackBuffer { #[allow(dead_code)] #[inline] + #[must_use] pub fn is_empty(&self) -> bool { self.buffer.is_empty() } #[inline] + #[must_use] pub fn is_full(&self) -> bool { self.len() == self.capacity() } #[inline] + #[must_use] pub fn len(&self) -> usize { self.buffer.len() } #[inline] + #[must_use] pub fn capacity(&self) -> usize { self.buffer.capacity() } diff --git a/commons/zenoh-config/Cargo.toml b/commons/zenoh-config/Cargo.toml index cd504be397..3bf62bfdb6 100644 --- a/commons/zenoh-config/Cargo.toml +++ b/commons/zenoh-config/Cargo.toml @@ -31,7 +31,6 @@ serde = { workspace = true, features = ["default"] } serde_json = { workspace = true } serde_yaml = { workspace = true } validated_struct = { workspace = true, features = ["json5", "json_get"] } -zenoh-cfg-properties = { workspace = true } zenoh-core = { workspace = true } zenoh-protocol = { workspace = true } zenoh-result = { workspace = true } diff --git a/commons/zenoh-config/src/defaults.rs b/commons/zenoh-config/src/defaults.rs index a7cafabe61..ea6714eb68 100644 --- a/commons/zenoh-config/src/defaults.rs +++ b/commons/zenoh-config/src/defaults.rs @@ -43,11 +43,11 @@ pub mod scouting { pub const interface: &str = "auto"; pub mod autoconnect { pub const router: &crate::WhatAmIMatcher = // "" - &crate::WhatAmIMatcher(unsafe { std::num::NonZeroU8::new_unchecked(128) }); + &crate::WhatAmIMatcher::empty(); pub const peer: &crate::WhatAmIMatcher = // "router|peer" - &crate::WhatAmIMatcher(unsafe { std::num::NonZeroU8::new_unchecked(131) }); + &crate::WhatAmIMatcher::empty().router().peer(); pub const client: &crate::WhatAmIMatcher = // "router|peer" - &crate::WhatAmIMatcher(unsafe { std::num::NonZeroU8::new_unchecked(131) }); + &crate::WhatAmIMatcher::empty().router().peer(); mode_accessor!(crate::WhatAmIMatcher); } pub mod listen { @@ -62,11 +62,11 @@ pub mod scouting { pub const multihop: bool = false; pub mod autoconnect { pub const router: &crate::WhatAmIMatcher = // "" - &crate::WhatAmIMatcher(unsafe { std::num::NonZeroU8::new_unchecked(128) }); + &crate::WhatAmIMatcher::empty(); pub const peer: &crate::WhatAmIMatcher = // "router|peer" - &crate::WhatAmIMatcher(unsafe { std::num::NonZeroU8::new_unchecked(131) }); + &crate::WhatAmIMatcher::empty().router().peer(); pub const client: &crate::WhatAmIMatcher = // "router|peer" - &crate::WhatAmIMatcher(unsafe { std::num::NonZeroU8::new_unchecked(131) }); + &crate::WhatAmIMatcher::empty().router().peer(); mode_accessor!(crate::WhatAmIMatcher); } } @@ -102,10 +102,10 @@ pub mod routing { impl Default for TransportUnicastConf { fn default() -> Self { Self { - accept_timeout: Some(10000), - accept_pending: Some(100), - max_sessions: Some(1000), - max_links: Some(1), + accept_timeout: 10_000, + accept_pending: 100, + max_sessions: 1_000, + max_links: 1, } } } @@ -130,12 +130,12 @@ impl Default for LinkTxConf { fn default() -> Self { let num = 1 + ((num_cpus::get() - 1) / 4); Self { - sequence_number_resolution: Some((2 as ZInt).pow(28)), - lease: Some(10000), - keep_alive: Some(4), - batch_size: Some(u16::MAX), + sequence_number_resolution: Bits::from(TransportSn::MAX), + lease: 10_000, + keep_alive: 4, + batch_size: BatchSize::MAX, queue: QueueConf::default(), - threads: Some(num), + threads: num, } } } @@ -144,7 +144,7 @@ impl Default for QueueConf { fn default() -> Self { Self { size: QueueSizeConf::default(), - backoff: Some(100), + backoff: 100, } } } @@ -163,8 +163,8 @@ impl Default for QueueSizeConf { interactive_high: 1, data_high: 2, data: 4, - data_low: 4, - background: 4, + data_low: 2, + background: 1, } } } @@ -172,8 +172,8 @@ impl Default for QueueSizeConf { impl Default for LinkRxConf { fn default() -> Self { Self { - buffer_size: Some(u16::MAX as usize), - max_message_size: Some(2_usize.pow(30)), + buffer_size: BatchSize::MAX as usize, + max_message_size: 2_usize.pow(30), } } } diff --git a/commons/zenoh-config/src/lib.rs b/commons/zenoh-config/src/lib.rs index 165a3a0eb1..5f4082e74d 100644 --- a/commons/zenoh-config/src/lib.rs +++ b/commons/zenoh-config/src/lib.rs @@ -21,6 +21,8 @@ use serde::{ Deserialize, Serialize, }; use serde_json::Value; +#[allow(unused_imports)] +use std::convert::TryFrom; // This is a false positive from the rust analyser use std::{ any::Any, collections::{HashMap, HashSet}, @@ -33,13 +35,14 @@ use std::{ }; use validated_struct::ValidatedMapAssociatedTypes; pub use validated_struct::{GetError, ValidatedMap}; -pub use zenoh_cfg_properties::config::*; use zenoh_core::zlock; -use zenoh_protocol::core::{ - key_expr::OwnedKeyExpr, - whatami::{WhatAmIMatcher, WhatAmIMatcherVisitor}, +pub use zenoh_protocol::core::{ + whatami, EndPoint, Locator, Priority, WhatAmI, WhatAmIMatcher, WhatAmIMatcherVisitor, ZenohId, +}; +use zenoh_protocol::{ + core::{key_expr::OwnedKeyExpr, Bits}, + transport::{BatchSize, TransportSn}, }; -pub use zenoh_protocol::core::{whatami, EndPoint, Locator, Priority, WhatAmI, ZenohId}; use zenoh_result::{bail, zerror, ZResult}; use zenoh_util::LibLoader; @@ -52,7 +55,6 @@ pub type ValidationFunction = std::sync::Arc< + Send + Sync, >; -type ZInt = u64; /// Creates an empty zenoh net Session configuration. pub fn empty() -> Config { @@ -176,7 +178,7 @@ validated_struct::validator! { }, /// The default timeout to apply to queries in milliseconds. - queries_default_timeout: Option, + queries_default_timeout: Option, /// The routing strategy to use and it's configuration. pub routing: #[derive(Default)] @@ -210,17 +212,17 @@ validated_struct::validator! { TransportConf { pub unicast: TransportUnicastConf { /// Timeout in milliseconds when opening a link (default: 10000). - accept_timeout: Option, + accept_timeout: u64, /// Number of links that may stay pending during accept phase (default: 100). - accept_pending: Option, + accept_pending: usize, /// Maximum number of unicast sessions (default: 1000) - max_sessions: Option, + max_sessions: usize, /// Maximum number of unicast incoming links per transport session (default: 1) - max_links: Option, + max_links: usize, }, pub multicast: TransportMulticastConf { /// Link join interval duration in milliseconds (default: 2500) - join_interval: Option, + join_interval: Option, /// Maximum number of multicast sessions (default: 1000) max_sessions: Option, }, @@ -235,15 +237,16 @@ validated_struct::validator! { // If not configured, all the supported protocols are automatically whitelisted. pub protocols: Option>, pub tx: LinkTxConf { - /// The largest value allowed for Zenoh message sequence numbers (wrappring to 0 when reached). When establishing a session with another Zenoh instance, the lowest value of the two instances will be used. - /// Defaults to 2^28. - sequence_number_resolution: Option, + /// The resolution in bits to be used for the message sequence numbers. + /// When establishing a session with another Zenoh instance, the lowest value of the two instances will be used. + /// Accepted values: 8bit, 16bit, 32bit, 64bit. + sequence_number_resolution: Bits where (sequence_number_resolution_validator), /// Link lease duration in milliseconds (default: 10000) - lease: Option, + lease: u64, /// Number fo keep-alive messages in a link lease duration (default: 4) - keep_alive: Option, + keep_alive: usize, /// Zenoh's MTU equivalent (default: 2^16-1) - batch_size: Option, + batch_size: BatchSize, pub queue: QueueConf { /// The size of each priority queue indicates the number of batches a given queue can contain. /// The amount of memory being allocated for each queue is then SIZE_XXX * BATCH_SIZE. @@ -262,10 +265,10 @@ validated_struct::validator! { } where (queue_size_validator), /// The initial exponential backoff time in nanoseconds to allow the batching to eventually progress. /// Higher values lead to a more aggressive batching but it will introduce additional latency. - backoff: Option + backoff: u64, }, // Number of threads used for TX - threads: Option, + threads: usize, }, pub rx: LinkRxConf { /// Receiving buffer size in bytes for each link @@ -273,10 +276,10 @@ validated_struct::validator! { /// For very high throughput scenarios, the rx_buffer_size can be increased to accomodate /// more in-flight data. This is particularly relevant when dealing with large messages. /// E.g. for 16MiB rx_buffer_size set the value to: 16777216. - buffer_size: Option, + buffer_size: usize, /// Maximum size of the defragmentation buffer at receiver end (default: 1GiB). /// Fragmented messages that are larger than the configured size will be dropped. - max_message_size: Option, + max_message_size: usize, }, pub tls: #[derive(Default)] TLSConf { @@ -288,6 +291,10 @@ validated_struct::validator! { client_certificate: Option, server_name_verification: Option }, + pub shared_memory: #[derive(Default)] + SHMConf { + shm_access_mask: Option + }, pub compression: #[derive(Default)] /// **Experimental** compression feature. /// Will compress the batches hop to hop (as opposed to end to end). May cause errors when @@ -301,7 +308,8 @@ validated_struct::validator! { enabled: bool, } }, - pub shared_memory: SharedMemoryConf { + pub shared_memory: + SharedMemoryConf { /// Whether shared memory is enabled or not. /// If set to `true`, the shared-memory transport will be enabled. (default `false`). enabled: bool, @@ -311,7 +319,7 @@ validated_struct::validator! { /// The configuration of authentification. /// A password implies a username is required. pub usrpwd: #[derive(Default)] - UserConf { + UsrPwdConf { user: Option, password: Option, /// The path to a file containing the user password dictionary, a file containing `:` @@ -407,15 +415,15 @@ fn config_deser() { assert_eq!(*config.scouting().multicast().enabled(), Some(false)); assert_eq!( config.scouting().multicast().autoconnect().router(), - Some(&WhatAmIMatcher::try_from(131).unwrap()) + Some(&WhatAmIMatcher::empty().router().peer()) ); assert_eq!( config.scouting().multicast().autoconnect().peer(), - Some(&WhatAmIMatcher::try_from(131).unwrap()) + Some(&WhatAmIMatcher::empty().router().peer()) ); assert_eq!( config.scouting().multicast().autoconnect().client(), - Some(&WhatAmIMatcher::try_from(131).unwrap()) + Some(&WhatAmIMatcher::empty().router().peer()) ); let config = Config::from_deserializer( &mut json5::Deserializer::from_str( @@ -434,11 +442,11 @@ fn config_deser() { assert_eq!(*config.scouting().multicast().enabled(), Some(false)); assert_eq!( config.scouting().multicast().autoconnect().router(), - Some(&WhatAmIMatcher::try_from(128).unwrap()) + Some(&WhatAmIMatcher::empty()) ); assert_eq!( config.scouting().multicast().autoconnect().peer(), - Some(&WhatAmIMatcher::try_from(131).unwrap()) + Some(&WhatAmIMatcher::empty().router().peer()) ); assert_eq!(config.scouting().multicast().autoconnect().client(), None); let config = Config::from_deserializer( @@ -691,7 +699,7 @@ where ) -> Result<>::Accessor, GetError> { let guard: MutexGuard<'a, T> = zlock!(self.inner.inner); - // Safety: MutexGuard pins the mutex behind which the value is held. + // SAFETY: MutexGuard pins the mutex behind which the value is held. let subref = guard.get(key.as_ref())? as *const _; Ok(GetGuard { _guard: guard, @@ -731,7 +739,7 @@ where ) -> Result<>::Accessor, GetError> { let guard: MutexGuard<'a, T> = zlock!(self.inner.inner); - // Safety: MutexGuard pins the mutex behind which the value is held. + // SAFETY: MutexGuard pins the mutex behind which the value is held. let subref = guard.get(key.as_ref())? as *const _; Ok(GetGuard { _guard: guard, @@ -765,6 +773,10 @@ impl<'a, T> AsRef for GetGuard<'a, T> { } } +fn sequence_number_resolution_validator(b: &Bits) -> bool { + b <= &Bits::from(TransportSn::MAX) +} + fn queue_size_validator(q: &QueueSizeConf) -> bool { fn check(size: &usize) -> bool { (QueueSizeConf::MIN..=QueueSizeConf::MAX).contains(size) @@ -790,7 +802,7 @@ fn queue_size_validator(q: &QueueSizeConf) -> bool { && check(background) } -fn user_conf_validator(u: &UserConf) -> bool { +fn user_conf_validator(u: &UsrPwdConf) -> bool { (u.password().is_none() && u.user().is_none()) || (u.password().is_some() && u.user().is_some()) } diff --git a/commons/zenoh-core/src/macros.rs b/commons/zenoh-core/src/macros.rs index 94b8e83dcc..b0cbb24963 100644 --- a/commons/zenoh-core/src/macros.rs +++ b/commons/zenoh-core/src/macros.rs @@ -64,6 +64,20 @@ macro_rules! zasyncread { }; } +// This macro performs an async read with upgrade to write option on RwLock +// For performance reasons, it first performs a try_upgradable_read() and, +// if it fails, it falls back on upgradable_read().await +#[macro_export] +macro_rules! zasyncread_upgradable { + ($var:expr) => { + if let Some(g) = $var.try_upgradable_read() { + g + } else { + $var.upgradable_read().await + } + }; +} + // This macro performs an async write on RwLock // For performance reasons, it first performs a try_write() and, // if it fails, it falls back on write().await @@ -140,17 +154,17 @@ macro_rules! zconfigurable { () => () } -// This macro is a shorthand for the conversion to ZInt +// This macro is a shorthand for the conversion to u64 // This macro requires to previously import the following: // use std::convert::TryFrom; #[macro_export] -macro_rules! to_zint { +macro_rules! to_u64 { ($val:expr) => { - ZInt::try_from($val).unwrap_or_else(|_| { + u64::try_from($val).unwrap_or_else(|_| { panic!( - "Can not encode {} as ZInt (max ZInt value: {})", + "Can not encode {} as u64 (max u64 value: {})", $val, - ZInt::MAX + u64::MAX ) }) }; @@ -191,3 +205,21 @@ macro_rules! zparse { }) }; } + +// This macro allows to do conditional compilation +#[macro_export] +macro_rules! zcondfeat { + ($feature:literal, $yes:expr, $not:expr) => {{ + { + #[cfg(feature = $feature)] + { + $yes + } + + #[cfg(not(feature = $feature))] + { + $not + } + } + }}; +} diff --git a/commons/zenoh-keyexpr/src/key_expr/canon.rs b/commons/zenoh-keyexpr/src/key_expr/canon.rs index 7470c882d2..00e79b0c08 100644 --- a/commons/zenoh-keyexpr/src/key_expr/canon.rs +++ b/commons/zenoh-keyexpr/src/key_expr/canon.rs @@ -125,6 +125,7 @@ impl Canonizable for String { #[test] fn canonizer() { use super::OwnedKeyExpr; + dbg!(OwnedKeyExpr::autocanonize(String::from("/a/b/")).unwrap_err()); dbg!(OwnedKeyExpr::autocanonize(String::from("/a/b")).unwrap_err()); dbg!(OwnedKeyExpr::autocanonize(String::from("a/b/")).unwrap_err()); diff --git a/commons/zenoh-keyexpr/src/keyexpr_tree/arc_tree.rs b/commons/zenoh-keyexpr/src/keyexpr_tree/arc_tree.rs index 84a011a8bf..4571ab050e 100644 --- a/commons/zenoh-keyexpr/src/keyexpr_tree/arc_tree.rs +++ b/commons/zenoh-keyexpr/src/keyexpr_tree/arc_tree.rs @@ -414,7 +414,7 @@ pub(crate) mod sealed { fn next(&mut self) -> Option { self.iter.next().map(|i| { Tokenized(i, unsafe { - // Safety: while this makes it possible for multiple mutable references to the Token to exist, + // SAFETY: while this makes it possible for multiple mutable references to the Token to exist, // it prevents them from being extracted and thus used to create multiple mutable references to // a same memory address. core::mem::transmute_copy(&self.token) diff --git a/commons/zenoh-protocol/Cargo.toml b/commons/zenoh-protocol/Cargo.toml index 7485c5ba9f..829c275804 100644 --- a/commons/zenoh-protocol/Cargo.toml +++ b/commons/zenoh-protocol/Cargo.toml @@ -36,10 +36,12 @@ std = [ "zenoh-result/std", ] test = ["rand", "zenoh-buffers/test"] -shared-memory = ["std"] +shared-memory = ["std", "zenoh-buffers/shared-memory"] +stats = [] complete_n = [] [dependencies] +const_format = { workspace = true } hex = { workspace = true, features = ["alloc"] } rand = { workspace = true, features = ["alloc", "getrandom"], optional = true } serde = { workspace = true, features = ["alloc"] } diff --git a/commons/zenoh-protocol/src/common/attachment.rs b/commons/zenoh-protocol/src/common/attachment.rs deleted file mode 100644 index 4f1922c6c5..0000000000 --- a/commons/zenoh-protocol/src/common/attachment.rs +++ /dev/null @@ -1,56 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use zenoh_buffers::ZBuf; - -/// # Attachment decorator -/// -/// ```text -/// NOTE: 16 bits (2 bytes) may be prepended to the serialized message indicating the total length -/// in bytes of the message, resulting in the maximum length of a message being 65_535 bytes. -/// This is necessary in those stream-oriented transports (e.g., TCP) that do not preserve -/// the boundary of the serialized messages. The length is encoded as little-endian. -/// In any case, the length of a message must not exceed 65_535 bytes. -/// -/// The Attachment can decorate any message (i.e., TransportMessage and ZenohMessage) and it allows to -/// append to the message any additional information. Since the information contained in the -/// Attachement is relevant only to the layer that provided them (e.g., Transport, Zenoh, User) it -/// is the duty of that layer to serialize and de-serialize the attachment whenever deemed necessary. -/// The attachement always contains serialized properties. -/// -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+-+-+-+-+-+ -/// |X|X|Z| ATTCH | -/// +-+-+-+---------+ -/// ~ Attachment ~ -/// +---------------+ -/// ``` -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Attachment { - pub buffer: ZBuf, -} - -impl Attachment { - pub fn new(buffer: ZBuf) -> Self { - Self { buffer } - } - - #[cfg(feature = "test")] - pub fn rand() -> Self { - use rand::Rng; - - Self { - buffer: ZBuf::rand(rand::thread_rng().gen_range(1..128)), - } - } -} diff --git a/commons/zenoh-protocol/src/common/extension.rs b/commons/zenoh-protocol/src/common/extension.rs new file mode 100644 index 0000000000..f61df61cc6 --- /dev/null +++ b/commons/zenoh-protocol/src/common/extension.rs @@ -0,0 +1,433 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use core::{ + convert::TryFrom, + fmt::{self, Debug}, +}; +use zenoh_buffers::ZBuf; + +/// # Zenoh extensions +/// +/// A zenoh extension is encoded as TLV (Type, Length, Value). +/// Zenoh extensions with unknown IDs (i.e., type) can be skipped by reading the length and +/// not decoding the body (i.e. value). In case the zenoh extension is unknown, it is +/// still possible to forward it to the next hops, which in turn may be able to understand it. +/// This results in the capability of introducing new extensions in an already running system +/// without requiring the redeployment of the totality of infrastructure nodes. +/// +/// The zenoh extension wire format is the following: +/// +/// ```text +/// Header flags: +/// - E |: Encoding The encoding of the extension +/// - E/ +/// - Z: More If Z==1 then another extension will follow. +/// +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// |Z|ENC|M| ID | +/// +-+---+-+-------+ +/// % length % -- If ENC == Z64 || ENC == ZBuf (z32) +/// +---------------+ +/// ~ [u8] ~ -- If ENC == ZBuf +/// +---------------+ +/// +/// Encoding: +/// - 0b00: Unit +/// - 0b01: Z64 +/// - 0b10: ZBuf +/// - 0b11: Reserved +/// +/// (*) If the zenoh extension is not understood, then it SHOULD NOT be dropped and it +/// SHOULD be forwarded to the next hops. +/// ``` +/// +pub mod iext { + use core::fmt; + + pub const ID_BITS: u8 = 4; + pub const ID_MASK: u8 = !(u8::MAX << ID_BITS); + + pub const FLAG_M: u8 = 1 << 4; + pub const ENC_UNIT: u8 = 0b00 << 5; + pub const ENC_Z64: u8 = 0b01 << 5; + pub const ENC_ZBUF: u8 = 0b10 << 5; + pub const ENC_MASK: u8 = 0b11 << 5; + pub const FLAG_Z: u8 = 1 << 7; + + pub const fn eid(header: u8) -> u8 { + header & !FLAG_Z + } + + pub const fn mid(header: u8) -> u8 { + header & ID_MASK + } + + pub(super) const fn id(id: u8, mandatory: bool, encoding: u8) -> u8 { + let mut id = id & ID_MASK; + if mandatory { + id |= FLAG_M; + } else { + id &= !FLAG_M; + } + id |= encoding; + id + } + + pub(super) const fn is_mandatory(id: u8) -> bool { + crate::common::imsg::has_flag(id, FLAG_M) + } + + pub(super) fn fmt(f: &mut fmt::DebugStruct, id: u8) { + f.field("Id", &(id & ID_MASK)) + .field("Mandatory", &is_mandatory(id)) + .field( + "Encoding", + match id & ENC_MASK { + ENC_UNIT => &"Unit", + ENC_Z64 => &"Z64", + ENC_ZBUF => &"ZBuf", + _ => &"Unknown", + }, + ); + } +} + +pub struct DidntConvert; + +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct ZExtUnit; + +impl ZExtUnit<{ ID }> { + pub const ID: u8 = ID; + + pub const fn new() -> Self { + Self + } + + pub const fn id(mandatory: bool) -> u8 { + iext::id(ID, mandatory, iext::ENC_UNIT) + } + + pub const fn is_mandatory(&self) -> bool { + iext::is_mandatory(ID) + } + + pub const fn transmute(self) -> ZExtUnit<{ DI }> { + ZExtUnit::new() + } + + #[cfg(feature = "test")] + pub fn rand() -> Self { + Self::new() + } +} + +impl Debug for ZExtUnit<{ ID }> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut s = f.debug_struct("ZExtUnit"); + iext::fmt(&mut s, ID); + s.finish() + } +} + +impl TryFrom for ZExtUnit<{ ID }> { + type Error = DidntConvert; + + fn try_from(v: ZExtUnknown) -> Result { + if v.id != ID { + return Err(DidntConvert); + } + match v.body { + ZExtBody::Unit => Ok(Self::new()), + _ => Err(DidntConvert), + } + } +} + +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct ZExtZ64 { + pub value: u64, +} + +impl ZExtZ64<{ ID }> { + pub const ID: u8 = ID; + + pub const fn new(value: u64) -> Self { + Self { value } + } + + pub const fn id(mandatory: bool) -> u8 { + iext::id(ID, mandatory, iext::ENC_Z64) + } + + pub const fn is_mandatory(&self) -> bool { + iext::is_mandatory(ID) + } + + pub const fn transmute(self) -> ZExtZ64<{ DI }> { + ZExtZ64::new(self.value) + } + + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + + let mut rng = rand::thread_rng(); + let value: u64 = rng.gen(); + Self { value } + } +} + +impl Debug for ZExtZ64<{ ID }> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut s = f.debug_struct("ZExtZ64"); + iext::fmt(&mut s, ID); + s.field("Value", &self.value).finish() + } +} + +impl TryFrom for ZExtZ64<{ ID }> { + type Error = DidntConvert; + + fn try_from(v: ZExtUnknown) -> Result { + if v.id != ID { + return Err(DidntConvert); + } + match v.body { + ZExtBody::Z64(v) => Ok(Self::new(v)), + _ => Err(DidntConvert), + } + } +} + +#[repr(transparent)] +#[derive(Clone, PartialEq, Eq)] +pub struct ZExtZBuf { + pub value: ZBuf, +} + +impl ZExtZBuf<{ ID }> { + pub const ID: u8 = ID; + + pub const fn new(value: ZBuf) -> Self { + Self { value } + } + + pub const fn id(mandatory: bool) -> u8 { + iext::id(ID, mandatory, iext::ENC_ZBUF) + } + + pub const fn is_mandatory(&self) -> bool { + iext::is_mandatory(ID) + } + + pub fn transmute(self) -> ZExtZBuf<{ DI }> { + ZExtZBuf::new(self.value) + } + + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + + let mut rng = rand::thread_rng(); + let value = ZBuf::rand(rng.gen_range(8..=64)); + Self { value } + } +} + +impl Debug for ZExtZBuf<{ ID }> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut s = f.debug_struct("ZExtZBuf"); + iext::fmt(&mut s, ID); + s.field("Value", &self.value).finish() + } +} + +impl TryFrom for ZExtZBuf<{ ID }> { + type Error = DidntConvert; + + fn try_from(v: ZExtUnknown) -> Result { + if v.id != ID { + return Err(DidntConvert); + } + match v.body { + ZExtBody::ZBuf(v) => Ok(Self::new(v)), + _ => Err(DidntConvert), + } + } +} + +#[derive(Clone, PartialEq, Eq)] +pub struct ZExtZBufHeader { + pub len: usize, +} + +impl ZExtZBufHeader<{ ID }> { + pub const ID: u8 = ID; + + pub const fn new(len: usize) -> Self { + Self { len } + } + + pub const fn id(mandatory: bool) -> u8 { + iext::id(ID, mandatory, iext::ENC_ZBUF) + } + + pub const fn is_mandatory(&self) -> bool { + iext::is_mandatory(ID) + } +} + +impl Debug for ZExtZBufHeader<{ ID }> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut s = f.debug_struct("ZExtZBufHeader"); + iext::fmt(&mut s, ID); + s.field("Len", &self.len).finish() + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ZExtBody { + Unit, + Z64(u64), + ZBuf(ZBuf), +} + +impl ZExtBody { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::{seq::SliceRandom, Rng}; + let mut rng = rand::thread_rng(); + [ + ZExtBody::Unit, + ZExtBody::Z64(rng.gen()), + ZExtBody::ZBuf(ZBuf::rand(rng.gen_range(8..=64))), + ] + .choose(&mut rng) + .unwrap() + .clone() + } +} + +#[derive(Clone, PartialEq, Eq)] +pub struct ZExtUnknown { + pub id: u8, + pub body: ZExtBody, +} + +impl ZExtUnknown { + pub const fn new(id: u8, mandatory: bool, body: ZExtBody) -> Self { + let enc = match &body { + ZExtBody::Unit => iext::ENC_UNIT, + ZExtBody::Z64(_) => iext::ENC_Z64, + ZExtBody::ZBuf(_) => iext::ENC_ZBUF, + }; + let id = iext::id(id, mandatory, enc); + Self { id, body } + } + + pub const fn is_mandatory(&self) -> bool { + iext::is_mandatory(self.id) + } + + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); + + let id: u8 = rng.gen_range(0x00..=iext::ID_MASK); + let mandatory = rng.gen_bool(0.5); + let body = ZExtBody::rand(); + Self::new(id, mandatory, body) + } + + #[cfg(feature = "test")] + pub fn rand2(start: u8, mandatory: bool) -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); + + let id: u8 = rng.gen_range(start..=iext::ID_MASK); + let body = ZExtBody::rand(); + Self::new(id, mandatory, body) + } +} + +impl Debug for ZExtUnknown { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut s = f.debug_struct("ZExtUnknown"); + iext::fmt(&mut s, self.id); + match &self.body { + ZExtBody::Unit => {} + ZExtBody::Z64(v) => { + s.field("Value", v); + } + ZExtBody::ZBuf(v) => { + s.field("Value", v); + } + }; + s.finish() + } +} + +impl From> for ZExtUnknown { + fn from(_: ZExtUnit<{ ID }>) -> Self { + ZExtUnknown { + id: ID, + body: ZExtBody::Unit, + } + } +} + +impl From> for ZExtUnknown { + fn from(e: ZExtZ64<{ ID }>) -> Self { + ZExtUnknown { + id: ID, + body: ZExtBody::Z64(e.value), + } + } +} + +impl From> for ZExtUnknown { + fn from(e: ZExtZBuf<{ ID }>) -> Self { + ZExtUnknown { + id: ID, + body: ZExtBody::ZBuf(e.value), + } + } +} + +// Macros +#[macro_export] +macro_rules! zextunit { + ($id:expr, $m:expr) => { + ZExtUnit<{ ZExtUnit::<$id>::id($m) }> + } +} + +#[macro_export] +macro_rules! zextz64 { + ($id:expr, $m:expr) => { + ZExtZ64<{ ZExtZ64::<$id>::id($m) }> + } +} + +#[macro_export] +macro_rules! zextzbuf { + ($id:expr, $m:expr) => { + ZExtZBuf<{ ZExtZBuf::<$id>::id($m) }> + } +} diff --git a/commons/zenoh-protocol/src/common/mod.rs b/commons/zenoh-protocol/src/common/mod.rs index 73d3ddd8b8..d11d0b0c52 100644 --- a/commons/zenoh-protocol/src/common/mod.rs +++ b/commons/zenoh-protocol/src/common/mod.rs @@ -11,30 +11,15 @@ // Contributors: // ZettaScale Zenoh Team, // -pub mod attachment; -pub use attachment::*; +pub mod extension; +pub use extension::*; /*************************************/ /* IDS */ /*************************************/ // Inner Message IDs pub mod imsg { - use crate::core::ZInt; - pub mod id { - // Transport Messages - pub const JOIN: u8 = 0x00; // For multicast communications only - pub const SCOUT: u8 = 0x01; - pub const HELLO: u8 = 0x02; - pub const INIT: u8 = 0x03; // For unicast communications only - pub const OPEN: u8 = 0x04; // For unicast communications only - pub const CLOSE: u8 = 0x05; - pub const SYNC: u8 = 0x06; - pub const ACK_NACK: u8 = 0x07; - pub const KEEP_ALIVE: u8 = 0x08; - pub const PING_PONG: u8 = 0x09; - pub const FRAME: u8 = 0x0a; - // Zenoh Messages pub const DECLARE: u8 = 0x0b; pub const DATA: u8 = 0x0c; @@ -47,26 +32,36 @@ pub mod imsg { pub const PRIORITY: u8 = 0x1c; pub const ROUTING_CONTEXT: u8 = 0x1d; pub const REPLY_CONTEXT: u8 = 0x1e; - pub const ATTACHMENT: u8 = 0x1f; } // Header mask pub const HEADER_BITS: u8 = 5; pub const HEADER_MASK: u8 = !(0xff << HEADER_BITS); - pub fn mid(header: u8) -> u8 { + pub const fn mid(header: u8) -> u8 { header & HEADER_MASK } - pub fn flags(header: u8) -> u8 { + pub const fn flags(header: u8) -> u8 { header & !HEADER_MASK } - pub fn has_flag(byte: u8, flag: u8) -> bool { + pub const fn has_flag(byte: u8, flag: u8) -> bool { byte & flag != 0 } - pub fn has_option(options: ZInt, flag: ZInt) -> bool { + pub const fn unset_flag(mut byte: u8, flag: u8) -> u8 { + byte &= !flag; + byte + } + + pub const fn set_flag(mut byte: u8, flag: u8) -> u8 { + byte = unset_flag(byte, flag); + byte |= flag; + byte + } + + pub const fn has_option(options: u64, flag: u64) -> bool { options & flag != 0 } } diff --git a/commons/zenoh-protocol/src/core/encoding.rs b/commons/zenoh-protocol/src/core/encoding.rs index 7af2ca6f70..f202b8e79c 100644 --- a/commons/zenoh-protocol/src/core/encoding.rs +++ b/commons/zenoh-protocol/src/core/encoding.rs @@ -11,13 +11,14 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::core::{CowStr, ZInt}; +use crate::core::CowStr; use alloc::{borrow::Cow, string::String}; use core::{ convert::TryFrom, fmt::{self, Debug}, mem, }; +use zenoh_result::{bail, zerror, ZError, ZResult}; mod consts { pub(super) const MIMES: [&str; 21] = [ @@ -74,50 +75,33 @@ pub enum KnownEncoding { impl From for u8 { fn from(val: KnownEncoding) -> Self { - unsafe { mem::transmute(val) } + val as u8 } } impl From for &str { fn from(val: KnownEncoding) -> Self { - consts::MIMES[usize::from(val)] - } -} - -impl From for usize { - fn from(val: KnownEncoding) -> Self { - u8::from(val) as usize + consts::MIMES[u8::from(val) as usize] } } impl TryFrom for KnownEncoding { - type Error = (); + type Error = ZError; fn try_from(value: u8) -> Result { if value < consts::MIMES.len() as u8 + 1 { Ok(unsafe { mem::transmute(value) }) } else { - Err(()) - } - } -} - -impl TryFrom for KnownEncoding { - type Error = (); - - fn try_from(value: ZInt) -> Result { - if value < consts::MIMES.len() as ZInt + 1 { - Ok(unsafe { mem::transmute(value as u8) }) - } else { - Err(()) + Err(zerror!("Unknown encoding")) } } } impl AsRef for KnownEncoding { fn as_ref(&self) -> &str { - consts::MIMES[usize::from(*self)] + consts::MIMES[u8::from(*self) as usize] } } + /// The encoding of a zenoh `zenoh::Value`. /// /// A zenoh encoding is a HTTP Mime type represented, for wire efficiency, @@ -129,26 +113,30 @@ pub enum Encoding { } impl Encoding { - pub fn new(prefix: ZInt, suffix: IntoCowStr) -> Option + pub fn new(prefix: u8, suffix: IntoCowStr) -> ZResult where IntoCowStr: Into> + AsRef, { - let prefix = KnownEncoding::try_from(prefix).ok()?; + let prefix = KnownEncoding::try_from(prefix)?; + let suffix = suffix.into(); + if suffix.as_bytes().len() > u8::MAX as usize { + bail!("Suffix length is limited to 255 characters") + } if suffix.as_ref().is_empty() { - Some(Encoding::Exact(prefix)) + Ok(Encoding::Exact(prefix)) } else { - Some(Encoding::WithSuffix(prefix, suffix.into().into())) + Ok(Encoding::WithSuffix(prefix, suffix.into())) } } /// Sets the suffix of this encoding. - pub fn with_suffix(self, suffix: IntoCowStr) -> Self + pub fn with_suffix(self, suffix: IntoCowStr) -> ZResult where IntoCowStr: Into> + AsRef, { match self { - Encoding::Exact(e) => Encoding::WithSuffix(e, suffix.into().into()), - Encoding::WithSuffix(e, s) => Encoding::WithSuffix(e, (s + suffix.as_ref()).into()), + Encoding::Exact(e) => Encoding::new(e as u8, suffix), + Encoding::WithSuffix(e, s) => Encoding::new(e as u8, s + suffix.as_ref()), } } @@ -290,7 +278,7 @@ impl Encoding { let mut rng = rand::thread_rng(); - let prefix: ZInt = rng.gen_range(0..20); + let prefix: u8 = rng.gen_range(0..20); let suffix: String = if rng.gen_bool(0.5) { let len = rng.gen_range(MIN..MAX); Alphanumeric.sample_string(&mut rng, len) diff --git a/commons/zenoh-protocol/src/core/endpoint.rs b/commons/zenoh-protocol/src/core/endpoint.rs index ef32f53f54..316e007476 100644 --- a/commons/zenoh-protocol/src/core/endpoint.rs +++ b/commons/zenoh-protocol/src/core/endpoint.rs @@ -12,10 +12,9 @@ // ZettaScale Zenoh Team, // use super::locator::*; -use crate::core::split_once; use alloc::{borrow::ToOwned, format, string::String, vec::Vec}; use core::{convert::TryFrom, fmt, str::FromStr}; -use zenoh_result::{zerror, Error as ZError, ZResult}; +use zenoh_result::{bail, zerror, Error as ZError, ZResult}; // Parsing chars pub const PROTO_SEPARATOR: char = '/'; @@ -24,6 +23,16 @@ pub const LIST_SEPARATOR: char = ';'; pub const FIELD_SEPARATOR: char = '='; pub const CONFIG_SEPARATOR: char = '#'; +fn split_once(s: &str, c: char) -> (&str, &str) { + match s.find(c) { + Some(index) => { + let (l, r) = s.split_at(index); + (l, &r[1..]) + } + None => (s, ""), + } +} + // Parsing functions pub(super) fn protocol(s: &str) -> &str { let pdix = s.find(PROTO_SEPARATOR).unwrap_or(s.len()); @@ -54,61 +63,69 @@ pub(super) fn config(s: &str) -> &str { } } -pub(super) fn read_properties(s: &str) -> impl Iterator + DoubleEndedIterator { - s.split(LIST_SEPARATOR).filter_map(|prop| { - if prop.is_empty() { - None - } else { - Some(split_once(prop, FIELD_SEPARATOR)) - } - }) -} +pub struct Parameters; -pub(super) fn write_properties<'s, I>(iter: I, into: &mut String) -where - I: Iterator, -{ - let mut first = true; - for (k, v) in iter { - if !first { - into.push(LIST_SEPARATOR); - } - into.push_str(k); - if !v.is_empty() { - into.push(FIELD_SEPARATOR); - into.push_str(v); +impl Parameters { + pub fn extend<'s, I>(iter: I, into: &mut String) + where + I: Iterator, + { + let mut first = into.is_empty(); + for (k, v) in iter { + if !first { + into.push(LIST_SEPARATOR); + } + into.push_str(k); + if !v.is_empty() { + into.push(FIELD_SEPARATOR); + into.push_str(v); + } + first = false; } - first = false; } -} -pub(super) fn extend_properties<'s, I>(iter: I, k: &'s str, v: &'s str) -> String -where - I: Iterator, -{ - let current = iter.filter(|x| x.0 != k); - let new = Some((k, v)).into_iter(); - let iter = current.chain(new); + pub fn iter(s: &str) -> impl Iterator + DoubleEndedIterator { + s.split(LIST_SEPARATOR).filter_map(|prop| { + if prop.is_empty() { + None + } else { + Some(split_once(prop, FIELD_SEPARATOR)) + } + }) + } - let mut into = String::new(); - write_properties(iter, &mut into); - into -} + pub fn get<'s>(s: &'s str, k: &str) -> Option<&'s str> { + Self::iter(s).find(|x| x.0 == k).map(|x| x.1) + } + + pub(super) fn insert<'s, I>(iter: I, k: &'s str, v: &'s str) -> String + where + I: Iterator, + { + let current = iter.filter(|x| x.0 != k); + let new = Some((k, v)).into_iter(); + let iter = current.chain(new); -pub(super) fn remove_properties<'s, I>(iter: I, k: &'s str) -> String -where - I: Iterator, -{ - let iter = iter.filter(|x| x.0 != k); + let mut into = String::new(); + Parameters::extend(iter, &mut into); + into + } + + pub(super) fn remove<'s, I>(iter: I, k: &'s str) -> String + where + I: Iterator, + { + let iter = iter.filter(|x| x.0 != k); - let mut into = String::new(); - write_properties(iter, &mut into); - into + let mut into = String::new(); + Parameters::extend(iter, &mut into); + into + } } // Protocol #[repr(transparent)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct Protocol<'a>(pub(super) &'a str); impl<'a> Protocol<'a> { @@ -129,8 +146,14 @@ impl fmt::Display for Protocol<'_> { } } +impl fmt::Debug for Protocol<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self) + } +} + #[repr(transparent)] -#[derive(Debug, PartialEq, Eq, Hash)] +#[derive(PartialEq, Eq, Hash)] pub struct ProtocolMut<'a>(&'a mut EndPoint); impl<'a> ProtocolMut<'a> { @@ -158,9 +181,15 @@ impl fmt::Display for ProtocolMut<'_> { } } +impl fmt::Debug for ProtocolMut<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self) + } +} + // Address #[repr(transparent)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct Address<'a>(pub(super) &'a str); impl<'a> Address<'a> { @@ -181,8 +210,14 @@ impl fmt::Display for Address<'_> { } } +impl fmt::Debug for Address<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self) + } +} + #[repr(transparent)] -#[derive(Debug, PartialEq, Eq, Hash)] +#[derive(PartialEq, Eq, Hash)] pub struct AddressMut<'a>(&'a mut EndPoint); impl<'a> AddressMut<'a> { @@ -210,9 +245,15 @@ impl fmt::Display for AddressMut<'_> { } } +impl fmt::Debug for AddressMut<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self) + } +} + // Metadata #[repr(transparent)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct Metadata<'a>(pub(super) &'a str); impl<'a> Metadata<'a> { @@ -225,11 +266,11 @@ impl<'a> Metadata<'a> { } pub fn iter(&'a self) -> impl Iterator + DoubleEndedIterator { - read_properties(self.0) + Parameters::iter(self.0) } pub fn get(&'a self, k: &str) -> Option<&'a str> { - self.iter().find(|x| x.0 == k).map(|x| x.1) + Parameters::get(self.0, k) } } @@ -245,8 +286,14 @@ impl fmt::Display for Metadata<'_> { } } +impl fmt::Debug for Metadata<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self) + } +} + #[repr(transparent)] -#[derive(Debug, PartialEq, Eq, Hash)] +#[derive(PartialEq, Eq, Hash)] pub struct MetadataMut<'a>(&'a mut EndPoint); impl<'a> MetadataMut<'a> { @@ -278,7 +325,7 @@ impl MetadataMut<'_> { let ep = EndPoint::new( self.0.protocol(), self.0.address(), - extend_properties(self.0.metadata().iter(), k, v), + Parameters::insert(self.0.metadata().iter(), k, v), self.0.config(), )?; @@ -290,7 +337,7 @@ impl MetadataMut<'_> { let ep = EndPoint::new( self.0.protocol(), self.0.address(), - remove_properties(self.0.metadata().iter(), k), + Parameters::remove(self.0.metadata().iter(), k), self.0.config(), )?; @@ -311,9 +358,15 @@ impl fmt::Display for MetadataMut<'_> { } } +impl fmt::Debug for MetadataMut<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self) + } +} + // Config #[repr(transparent)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct Config<'a>(pub(super) &'a str); impl<'a> Config<'a> { @@ -326,11 +379,11 @@ impl<'a> Config<'a> { } pub fn iter(&'a self) -> impl Iterator + DoubleEndedIterator { - read_properties(self.0) + Parameters::iter(self.0) } pub fn get(&'a self, k: &str) -> Option<&'a str> { - self.iter().find(|x| x.0 == k).map(|x| x.1) + Parameters::get(self.0, k) } } @@ -346,8 +399,14 @@ impl fmt::Display for Config<'_> { } } +impl fmt::Debug for Config<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self) + } +} + #[repr(transparent)] -#[derive(Debug, PartialEq, Eq, Hash)] +#[derive(PartialEq, Eq, Hash)] pub struct ConfigMut<'a>(&'a mut EndPoint); impl<'a> ConfigMut<'a> { @@ -380,7 +439,7 @@ impl ConfigMut<'_> { self.0.protocol(), self.0.address(), self.0.metadata(), - extend_properties(self.0.config().iter(), k, v), + Parameters::insert(self.0.config().iter(), k, v), )?; self.0.inner = ep.inner; @@ -392,7 +451,7 @@ impl ConfigMut<'_> { self.0.protocol(), self.0.address(), self.0.metadata(), - remove_properties(self.0.config().iter(), k), + Parameters::remove(self.0.config().iter(), k), )?; self.0.inner = ep.inner; @@ -411,8 +470,15 @@ impl fmt::Display for ConfigMut<'_> { f.write_str(self.as_str()) } } + +impl fmt::Debug for ConfigMut<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self) + } +} + /// A `String` that respects the [`EndPoint`] canon form: `#`, such that `` is a valid [`Locator`] `` is of the form `=;...;=` where keys are alphabetically sorted. -#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] #[serde(into = "String")] #[serde(try_from = "String")] pub struct EndPoint { @@ -432,6 +498,11 @@ impl EndPoint { let m: &str = metadata.as_ref(); let c: &str = config.as_ref(); + let len = p.as_bytes().len() + a.as_bytes().len() + m.as_bytes().len(); + if len > u8::MAX as usize { + bail!("Endpoint too big: {} bytes. Max: {} bytes. ", len, u8::MAX); + } + let s = match (m.is_empty(), c.is_empty()) { (true, true) => format!("{p}{PROTO_SEPARATOR}{a}"), (false, true) => format!("{p}{PROTO_SEPARATOR}{a}{METADATA_SEPARATOR}{m}"), @@ -506,6 +577,12 @@ impl fmt::Display for EndPoint { } } +impl fmt::Debug for EndPoint { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self) + } +} + impl From for String { fn from(v: EndPoint) -> String { v.inner @@ -602,7 +679,7 @@ impl EndPoint { }; const MIN: usize = 2; - const MAX: usize = 16; + const MAX: usize = 8; fn gen_hashmap(rng: &mut ThreadRng, endpoint: &mut String) { let num = rng.gen_range(MIN..MAX); diff --git a/commons/zenoh-protocol/src/core/locator.rs b/commons/zenoh-protocol/src/core/locator.rs index db250ba991..cdd3dfa64c 100644 --- a/commons/zenoh-protocol/src/core/locator.rs +++ b/commons/zenoh-protocol/src/core/locator.rs @@ -19,7 +19,7 @@ use zenoh_result::{Error as ZError, ZResult}; // Locator /// A `String` that respects the [`Locator`] canon form: `/
[?]`, /// such that `` is of the form `=;...;=` where keys are alphabetically sorted. -#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] #[serde(into = "String")] #[serde(try_from = "String")] pub struct Locator(pub(super) EndPoint); @@ -110,6 +110,12 @@ impl fmt::Display for Locator { } } +impl fmt::Debug for Locator { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self) + } +} + impl Locator { #[cfg(feature = "test")] pub fn rand() -> Self { diff --git a/commons/zenoh-protocol/src/core/mod.rs b/commons/zenoh-protocol/src/core/mod.rs index 0547bed86f..2547034c44 100644 --- a/commons/zenoh-protocol/src/core/mod.rs +++ b/commons/zenoh-protocol/src/core/mod.rs @@ -22,7 +22,6 @@ use core::{ convert::{From, TryFrom, TryInto}, fmt, hash::Hash, - num::NonZeroU64, str::FromStr, }; pub use uhlc::{Timestamp, NTP64}; @@ -32,27 +31,14 @@ use zenoh_result::{bail, zerror}; /// The unique Id of the [`HLC`](uhlc::HLC) that generated the concerned [`Timestamp`]. pub type TimestampId = uhlc::ID; -/// A zenoh integer. -pub type ZInt = u64; -pub type ZiInt = i64; -pub type NonZeroZInt = NonZeroU64; -pub const ZINT_MAX_BYTES: usize = 10; - -// WhatAmI values -pub type WhatAmI = whatami::WhatAmI; - /// Constants and helpers for zenoh `whatami` flags. pub mod whatami; - -/// A numerical Id mapped to a key expression. -pub type ExprId = ZInt; - -pub const EMPTY_EXPR_ID: ExprId = 0; +pub use whatami::*; pub use zenoh_keyexpr::key_expr; pub mod wire_expr; -pub use wire_expr::WireExpr; +pub use wire_expr::*; mod cowstr; pub use cowstr::CowStr; @@ -60,13 +46,17 @@ mod encoding; pub use encoding::{Encoding, KnownEncoding}; pub mod locator; -pub use locator::Locator; +pub use locator::*; + pub mod endpoint; -pub use endpoint::EndPoint; +pub use endpoint::*; + +pub mod resolution; +pub use resolution::*; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Property { - pub key: ZInt, + pub key: u64, pub value: Vec, } @@ -90,9 +80,9 @@ impl fmt::Display for SampleKind { } } -impl TryFrom for SampleKind { - type Error = ZInt; - fn try_from(kind: ZInt) -> Result { +impl TryFrom for SampleKind { + type Error = u64; + fn try_from(kind: u64) -> Result { match kind { 0 => Ok(SampleKind::Put), 1 => Ok(SampleKind::Delete), @@ -246,7 +236,7 @@ impl From<&ZenohId> for uhlc::ID { impl From for OwnedKeyExpr { fn from(zid: ZenohId) -> Self { - // Safety: zid.to_string() returns an stringified hexadecimal + // SAFETY: zid.to_string() returns an stringified hexadecimal // representation of the zid. Therefore, building a OwnedKeyExpr // by calling from_string_unchecked() is safe because it is // guaranteed that no wildcards nor reserved chars will be present. @@ -309,8 +299,8 @@ impl<'de> serde::Deserialize<'de> for ZenohId { } } -#[derive(Debug, Default, Copy, Clone, Eq, Hash, PartialEq)] #[repr(u8)] +#[derive(Debug, Default, Copy, Clone, Eq, Hash, PartialEq)] pub enum Priority { Control = 0, RealTime = 1, @@ -335,8 +325,8 @@ impl Priority { impl TryFrom for Priority { type Error = zenoh_result::Error; - fn try_from(conduit: u8) -> Result { - match conduit { + fn try_from(v: u8) -> Result { + match v { 0 => Ok(Priority::Control), 1 => Ok(Priority::RealTime), 2 => Ok(Priority::InteractiveHigh), @@ -363,63 +353,34 @@ pub enum Reliability { Reliable, } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] -pub struct Channel { - pub priority: Priority, - pub reliability: Reliability, -} +impl Reliability { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum ConduitSnList { - Plain(ConduitSn), - QoS(Box<[ConduitSn; Priority::NUM]>), -} + let mut rng = rand::thread_rng(); -impl fmt::Display for ConduitSnList { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "[ ")?; - match self { - ConduitSnList::Plain(sn) => { - write!( - f, - "{:?} {{ reliable: {}, best effort: {} }}", - Priority::default(), - sn.reliable, - sn.best_effort - )?; - } - ConduitSnList::QoS(ref sns) => { - for (prio, sn) in sns.iter().enumerate() { - let p: Priority = (prio as u8).try_into().unwrap(); - write!( - f, - "{:?} {{ reliable: {}, best effort: {} }}", - p, sn.reliable, sn.best_effort - )?; - if p != Priority::Background { - write!(f, ", ")?; - } - } - } + if rng.gen_bool(0.5) { + Reliability::Reliable + } else { + Reliability::BestEffort } - write!(f, " ]") } } -/// The kind of reliability. #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] -pub struct ConduitSn { - pub reliable: ZInt, - pub best_effort: ZInt, +pub struct Channel { + pub priority: Priority, + pub reliability: Reliability, } /// The kind of congestion control. #[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] #[repr(u8)] pub enum CongestionControl { - Block, #[default] - Drop, + Drop = 0, + Block = 1, } /// The subscription mode. @@ -427,8 +388,8 @@ pub enum CongestionControl { #[repr(u8)] pub enum SubMode { #[default] - Push, - Pull, + Push = 0, + Pull = 1, } #[derive(Debug, Clone, PartialEq, Eq, Default)] @@ -439,8 +400,8 @@ pub struct SubInfo { #[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] pub struct QueryableInfo { - pub complete: ZInt, // Default 0: incomplete - pub distance: ZInt, // Default 0: no distance + pub complete: u64, // Default 0: incomplete + pub distance: u64, // Default 0: no distance } /// The kind of consolidation. @@ -468,15 +429,5 @@ pub enum QueryTarget { All, AllComplete, #[cfg(feature = "complete_n")] - Complete(ZInt), -} - -pub(crate) fn split_once(s: &str, c: char) -> (&str, &str) { - match s.find(c) { - Some(index) => { - let (l, r) = s.split_at(index); - (l, &r[1..]) - } - None => (s, ""), - } + Complete(u64), } diff --git a/commons/zenoh-protocol/src/core/resolution.rs b/commons/zenoh-protocol/src/core/resolution.rs new file mode 100644 index 0000000000..a174ecdc9d --- /dev/null +++ b/commons/zenoh-protocol/src/core/resolution.rs @@ -0,0 +1,220 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::{network::RequestId, transport::TransportSn}; +use alloc::string::String; +use core::{fmt, str::FromStr}; +use zenoh_result::{bail, ZError}; + +#[repr(u8)] +// The value represents the 2-bit encoded value +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum Bits { + U8 = 0b00, + U16 = 0b01, + U32 = 0b10, + U64 = 0b11, +} + +impl Bits { + const S8: &str = "8bit"; + const S16: &str = "16bit"; + const S32: &str = "32bit"; + const S64: &str = "64bit"; + + pub const fn bits(&self) -> u32 { + match self { + Bits::U8 => u8::BITS, + Bits::U16 => u16::BITS, + Bits::U32 => u32::BITS, + Bits::U64 => u64::BITS, + } + } + + pub const fn mask(&self) -> u64 { + match self { + Bits::U8 => u8::MAX as u64, + Bits::U16 => u16::MAX as u64, + Bits::U32 => u32::MAX as u64, + Bits::U64 => u64::MAX, + } + } + + pub const fn to_str(self) -> &'static str { + match self { + Bits::U8 => Self::S8, + Bits::U16 => Self::S16, + Bits::U32 => Self::S32, + Bits::U64 => Self::S64, + } + } +} + +impl From for Bits { + fn from(_: u8) -> Self { + Self::U8 + } +} + +impl From for Bits { + fn from(_: u16) -> Self { + Self::U16 + } +} + +impl From for Bits { + fn from(_: u32) -> Self { + Self::U32 + } +} + +impl From for Bits { + fn from(_: u64) -> Self { + Self::U64 + } +} + +impl FromStr for Bits { + type Err = ZError; + + fn from_str(s: &str) -> Result { + match s { + Bits::S8 => Ok(Bits::U8), + Bits::S16 => Ok(Bits::U16), + Bits::S32 => Ok(Bits::U32), + Bits::S64 => Ok(Bits::U64), + _ => bail!( + "{s} is not a valid Bits value. Valid values are: '{}', '{}', '{}', '{}'.", + Bits::S8, + Bits::S16, + Bits::S32, + Bits::S64 + ), + } + } +} + +impl fmt::Display for Bits { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.to_str()) + } +} + +#[repr(u8)] +// The value indicates the bit offest +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Field { + FrameSN = 0, + RequestID = 2, +} + +#[repr(transparent)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct Resolution(u8); + +impl Resolution { + pub const fn as_u8(&self) -> u8 { + self.0 + } + + pub const fn get(&self, field: Field) -> Bits { + let value = (self.0 >> (field as u8)) & 0b11; + unsafe { core::mem::transmute(value) } + } + + pub fn set(&mut self, field: Field, bits: Bits) { + self.0 &= !(0b11 << field as u8); // Clear bits + self.0 |= (bits as u8) << (field as u8); // Set bits + } + + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + + let mut rng = rand::thread_rng(); + let v: u8 = rng.gen(); + Self(v & 0b00001111) + } +} + +impl Default for Resolution { + fn default() -> Self { + let frame_sn = Bits::from(TransportSn::MAX) as u8; + let request_id = (Bits::from(RequestId::MAX) as u8) << 2; + Self(frame_sn | request_id) + } +} + +impl From for Resolution { + fn from(v: u8) -> Self { + Self(v) + } +} + +// Serde +impl serde::Serialize for Bits { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(self.to_str()) + } +} + +pub struct BitsVisitor; +impl<'de> serde::de::Visitor<'de> for BitsVisitor { + type Value = Bits; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!( + formatter, + "either '{}', '{}', '{}', '{}'.", + Bits::S8, + Bits::S16, + Bits::S32, + Bits::S64, + ) + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + v.parse().map_err(|_| { + serde::de::Error::unknown_variant(v, &[Bits::S8, Bits::S16, Bits::S32, Bits::S64]) + }) + } + + fn visit_borrowed_str(self, v: &'de str) -> Result + where + E: serde::de::Error, + { + self.visit_str(v) + } + + fn visit_string(self, v: String) -> Result + where + E: serde::de::Error, + { + self.visit_str(&v) + } +} + +impl<'de> serde::Deserialize<'de> for Bits { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_str(BitsVisitor) + } +} diff --git a/commons/zenoh-protocol/src/core/whatami.rs b/commons/zenoh-protocol/src/core/whatami.rs index 2a9dfd7780..faeb4712e0 100644 --- a/commons/zenoh-protocol/src/core/whatami.rs +++ b/commons/zenoh-protocol/src/core/whatami.rs @@ -11,60 +11,74 @@ // Contributors: // ZettaScale Zenoh Team, // -use super::ZInt; use alloc::string::String; -use core::{convert::TryInto, fmt, num::NonZeroU8, ops::BitOr, str::FromStr}; +use const_format::formatcp; +use core::{convert::TryFrom, fmt, num::NonZeroU8, ops::BitOr, str::FromStr}; use zenoh_result::{bail, ZError}; #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum WhatAmI { - Router = 1, - Peer = 1 << 1, - Client = 1 << 2, + Router = 0b001, + Peer = 0b010, + Client = 0b100, } -impl FromStr for WhatAmI { - type Err = ZError; +impl WhatAmI { + const STR_R: &str = "router"; + const STR_P: &str = "peer"; + const STR_C: &str = "client"; - fn from_str(s: &str) -> Result { - match s { - "router" => Ok(WhatAmI::Router), - "peer" => Ok(WhatAmI::Peer), - "client" => Ok(WhatAmI::Client), - _ => bail!("{} is not a valid WhatAmI value. Valid values are: [\"router\", \"peer\", \"client\"].", s), + const U8_R: u8 = Self::Router as u8; + const U8_P: u8 = Self::Peer as u8; + const U8_C: u8 = Self::Client as u8; + + pub const fn to_str(self) -> &'static str { + match self { + Self::Router => Self::STR_R, + Self::Peer => Self::STR_P, + Self::Client => Self::STR_C, } } -} -impl WhatAmI { #[cfg(feature = "test")] pub fn rand() -> Self { use rand::prelude::SliceRandom; - let mut rng = rand::thread_rng(); - *[WhatAmI::Router, WhatAmI::Peer, WhatAmI::Client] + + *[Self::Router, Self::Peer, Self::Client] .choose(&mut rng) .unwrap() } +} - pub fn to_str(self) -> &'static str { - match self { - WhatAmI::Router => "router", - WhatAmI::Peer => "peer", - WhatAmI::Client => "client", +impl TryFrom for WhatAmI { + type Error = (); + + fn try_from(v: u8) -> Result { + match v { + Self::U8_R => Ok(Self::Router), + Self::U8_P => Ok(Self::Peer), + Self::U8_C => Ok(Self::Client), + _ => Err(()), } } +} + +impl FromStr for WhatAmI { + type Err = ZError; - pub fn try_from(value: ZInt) -> Option { - const CLIENT: ZInt = WhatAmI::Client as ZInt; - const ROUTER: ZInt = WhatAmI::Router as ZInt; - const PEER: ZInt = WhatAmI::Peer as ZInt; - match value { - CLIENT => Some(WhatAmI::Client), - ROUTER => Some(WhatAmI::Router), - PEER => Some(WhatAmI::Peer), - _ => None, + fn from_str(s: &str) -> Result { + match s { + Self::STR_R => Ok(Self::Router), + Self::STR_P => Ok(Self::Peer), + Self::STR_C => Ok(Self::Client), + _ => bail!( + "{s} is not a valid WhatAmI value. Valid values are: {}, {}, {}.", + Self::STR_R, + Self::STR_P, + Self::STR_C + ), } } } @@ -75,104 +89,97 @@ impl fmt::Display for WhatAmI { } } -impl serde::Serialize for WhatAmI { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(self.to_str()) +impl From for u8 { + fn from(w: WhatAmI) -> Self { + w as u8 } } -pub struct WhatAmIVisitor; +#[repr(transparent)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct WhatAmIMatcher(NonZeroU8); -impl<'de> serde::de::Visitor<'de> for WhatAmIVisitor { - type Value = WhatAmI; +impl WhatAmIMatcher { + // We use the 7th bit for detecting whether the WhatAmIMatcher is non-zero + const U8_0: u8 = 1 << 7; + const U8_R: u8 = Self::U8_0 | WhatAmI::U8_R; + const U8_P: u8 = Self::U8_0 | WhatAmI::U8_P; + const U8_C: u8 = Self::U8_0 | WhatAmI::U8_C; + const U8_R_P: u8 = Self::U8_0 | WhatAmI::U8_R | WhatAmI::U8_P; + const U8_P_C: u8 = Self::U8_0 | WhatAmI::U8_P | WhatAmI::U8_C; + const U8_R_C: u8 = Self::U8_0 | WhatAmI::U8_R | WhatAmI::U8_C; + const U8_R_P_C: u8 = Self::U8_0 | WhatAmI::U8_R | WhatAmI::U8_P | WhatAmI::U8_C; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("either 'router', 'client' or 'peer'") + pub const fn empty() -> Self { + Self(unsafe { NonZeroU8::new_unchecked(Self::U8_0) }) } - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, - { - v.parse() - .map_err(|_| serde::de::Error::unknown_variant(v, &["router", "client", "peer"])) + + pub const fn router(self) -> Self { + Self(unsafe { NonZeroU8::new_unchecked(self.0.get() | Self::U8_R) }) } - fn visit_borrowed_str(self, v: &'de str) -> Result - where - E: serde::de::Error, - { - self.visit_str(v) + + pub const fn peer(self) -> Self { + Self(unsafe { NonZeroU8::new_unchecked(self.0.get() | Self::U8_P) }) } - fn visit_string(self, v: String) -> Result - where - E: serde::de::Error, - { - self.visit_str(&v) + + pub const fn client(self) -> Self { + Self(unsafe { NonZeroU8::new_unchecked(self.0.get() | Self::U8_C) }) } -} -impl<'de> serde::Deserialize<'de> for WhatAmI { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - deserializer.deserialize_str(WhatAmIVisitor) + pub const fn is_empty(&self) -> bool { + self.0.get() == Self::U8_0 } -} -impl From for ZInt { - fn from(w: WhatAmI) -> Self { - w as ZInt + pub const fn matches(&self, w: WhatAmI) -> bool { + (self.0.get() & w as u8) != 0 } -} -#[repr(transparent)] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct WhatAmIMatcher(pub NonZeroU8); + pub const fn to_str(self) -> &'static str { + match self.0.get() { + Self::U8_0 => "", + Self::U8_R => WhatAmI::STR_R, + Self::U8_P => WhatAmI::STR_P, + Self::U8_C => WhatAmI::STR_C, + Self::U8_R_P => formatcp!("{}|{}", WhatAmI::STR_R, WhatAmI::STR_P), + Self::U8_R_C => formatcp!("{}|{}", WhatAmI::STR_R, WhatAmI::STR_C), + Self::U8_P_C => formatcp!("{}|{}", WhatAmI::STR_P, WhatAmI::STR_C), + Self::U8_R_P_C => formatcp!("{}|{}|{}", WhatAmI::STR_R, WhatAmI::STR_P, WhatAmI::STR_C), + _ => unreachable!(), + } + } -impl WhatAmIMatcher { #[cfg(feature = "test")] pub fn rand() -> Self { use rand::Rng; let mut rng = rand::thread_rng(); - WhatAmIMatcher(unsafe { NonZeroU8::new_unchecked(rng.gen_range(128..136)) }) - } - - pub fn try_from>(i: T) -> Option { - let i = i.try_into().ok()?; - if 127 < i && i < 136 { - Some(WhatAmIMatcher(unsafe { NonZeroU8::new_unchecked(i) })) - } else { - None + let mut waim = WhatAmIMatcher::empty(); + if rng.gen_bool(0.5) { + waim = waim.router(); } + if rng.gen_bool(0.5) { + waim = waim.peer(); + } + if rng.gen_bool(0.5) { + waim = waim.client(); + } + waim } +} - pub fn is_empty(self) -> bool { - self.0.get() == 128 - } - - pub fn empty() -> Self { - WhatAmIMatcher(unsafe { NonZeroU8::new_unchecked(128) }) - } +impl TryFrom for WhatAmIMatcher { + type Error = (); - pub fn matches(self, w: WhatAmI) -> bool { - (self.0.get() & w as u8) != 0 - } + fn try_from(v: u8) -> Result { + const MIN: u8 = 0; + const MAX: u8 = WhatAmI::U8_R | WhatAmI::U8_P | WhatAmI::U8_C; - pub fn to_str(self) -> &'static str { - match self.0.get() { - 128 => "", - 129 => "router", - 130 => "peer", - 132 => "client", - 131 => "router|peer", - 134 => "client|peer", - 133 => "client|router", - 135 => "client|router|peer", - _ => "invalid_matcher", + if (MIN..=MAX).contains(&v) { + Ok(WhatAmIMatcher(unsafe { + NonZeroU8::new_unchecked(Self::U8_0 | v) + })) + } else { + Err(()) } } } @@ -181,21 +188,77 @@ impl FromStr for WhatAmIMatcher { type Err = (); fn from_str(s: &str) -> Result { - let mut inner = 128; + let mut inner = 0; for s in s.split('|') { match s.trim() { "" => {} - "router" => inner |= WhatAmI::Router as u8, - "client" => inner |= WhatAmI::Client as u8, - "peer" => inner |= WhatAmI::Peer as u8, + WhatAmI::STR_R => inner |= WhatAmI::U8_R, + WhatAmI::STR_P => inner |= WhatAmI::U8_P, + WhatAmI::STR_C => inner |= WhatAmI::U8_C, _ => return Err(()), } } - Self::try_from(inner).ok_or(()) + Self::try_from(inner) } } -impl serde::Serialize for WhatAmIMatcher { +impl fmt::Display for WhatAmIMatcher { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.to_str()) + } +} + +impl From for u8 { + fn from(w: WhatAmIMatcher) -> u8 { + w.0.get() + } +} + +impl BitOr for WhatAmIMatcher +where + NonZeroU8: BitOr, +{ + type Output = Self; + + fn bitor(self, rhs: T) -> Self::Output { + WhatAmIMatcher(self.0 | rhs) + } +} + +impl BitOr for WhatAmIMatcher { + type Output = Self; + + fn bitor(self, rhs: WhatAmI) -> Self::Output { + self | rhs as u8 + } +} + +impl BitOr for WhatAmIMatcher { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self::Output { + self | rhs.0 + } +} + +impl BitOr for WhatAmI { + type Output = WhatAmIMatcher; + + fn bitor(self, rhs: Self) -> Self::Output { + WhatAmIMatcher(unsafe { + NonZeroU8::new_unchecked(self as u8 | rhs as u8 | WhatAmIMatcher::U8_0) + }) + } +} + +impl From for WhatAmIMatcher { + fn from(w: WhatAmI) -> Self { + WhatAmIMatcher(unsafe { NonZeroU8::new_unchecked(w as u8 | WhatAmIMatcher::U8_0) }) + } +} + +// Serde +impl serde::Serialize for WhatAmI { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, @@ -204,21 +267,26 @@ impl serde::Serialize for WhatAmIMatcher { } } -pub struct WhatAmIMatcherVisitor; -impl<'de> serde::de::Visitor<'de> for WhatAmIMatcherVisitor { - type Value = WhatAmIMatcher; +pub struct WhatAmIVisitor; + +impl<'de> serde::de::Visitor<'de> for WhatAmIVisitor { + type Value = WhatAmI; + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a | separated list of whatami variants ('peer', 'client' or 'router')") + write!( + formatter, + "either '{}', '{}' or '{}'", + WhatAmI::STR_R, + WhatAmI::STR_P, + WhatAmI::STR_C + ) } fn visit_str(self, v: &str) -> Result where E: serde::de::Error, { v.parse().map_err(|_| { - serde::de::Error::invalid_value( - serde::de::Unexpected::Str(v), - &"a | separated list of whatami variants ('peer', 'client' or 'router')", - ) + serde::de::Error::unknown_variant(v, &[WhatAmI::STR_R, WhatAmI::STR_P, WhatAmI::STR_C]) }) } fn visit_borrowed_str(self, v: &'de str) -> Result @@ -235,60 +303,73 @@ impl<'de> serde::de::Visitor<'de> for WhatAmIMatcherVisitor { } } -impl<'de> serde::Deserialize<'de> for WhatAmIMatcher { +impl<'de> serde::Deserialize<'de> for WhatAmI { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { - deserializer.deserialize_str(WhatAmIMatcherVisitor) - } -} - -impl fmt::Display for WhatAmIMatcher { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.to_str()) + deserializer.deserialize_str(WhatAmIVisitor) } } -impl From for ZInt { - fn from(w: WhatAmIMatcher) -> ZInt { - w.0.get() as ZInt +impl serde::Serialize for WhatAmIMatcher { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(self.to_str()) } } -impl BitOr for WhatAmIMatcher -where - NonZeroU8: BitOr, -{ - type Output = Self; - fn bitor(self, rhs: T) -> Self::Output { - WhatAmIMatcher(self.0 | rhs) +pub struct WhatAmIMatcherVisitor; +impl<'de> serde::de::Visitor<'de> for WhatAmIMatcherVisitor { + type Value = WhatAmIMatcher; + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!( + formatter, + "a | separated list of whatami variants ('{}', '{}', '{}')", + WhatAmI::STR_R, + WhatAmI::STR_P, + WhatAmI::STR_C + ) } -} -impl BitOr for WhatAmIMatcher { - type Output = Self; - fn bitor(self, rhs: WhatAmI) -> Self::Output { - self | rhs as u8 + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + v.parse().map_err(|_| { + serde::de::Error::invalid_value( + serde::de::Unexpected::Str(v), + &formatcp!( + "a | separated list of whatami variants ('{}', '{}', '{}')", + WhatAmI::STR_R, + WhatAmI::STR_P, + WhatAmI::STR_C + ), + ) + }) } -} -impl BitOr for WhatAmIMatcher { - type Output = Self; - fn bitor(self, rhs: Self) -> Self::Output { - self | rhs.0 + fn visit_borrowed_str(self, v: &'de str) -> Result + where + E: serde::de::Error, + { + self.visit_str(v) } -} - -impl BitOr for WhatAmI { - type Output = WhatAmIMatcher; - fn bitor(self, rhs: Self) -> Self::Output { - WhatAmIMatcher(unsafe { NonZeroU8::new_unchecked(self as u8 | rhs as u8 | 128) }) + fn visit_string(self, v: String) -> Result + where + E: serde::de::Error, + { + self.visit_str(&v) } } -impl From for WhatAmIMatcher { - fn from(w: WhatAmI) -> Self { - WhatAmIMatcher(unsafe { NonZeroU8::new_unchecked(w as u8 | 128) }) +impl<'de> serde::Deserialize<'de> for WhatAmIMatcher { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_str(WhatAmIMatcherVisitor) } } diff --git a/commons/zenoh-protocol/src/core/wire_expr.rs b/commons/zenoh-protocol/src/core/wire_expr.rs index 0b7cb409ce..7b0dee7471 100644 --- a/commons/zenoh-protocol/src/core/wire_expr.rs +++ b/commons/zenoh-protocol/src/core/wire_expr.rs @@ -13,17 +13,25 @@ // //! This module defines the wire representation of Key Expressions. -use crate::core::ExprId; use alloc::{ borrow::Cow, string::{String, ToString}, }; -use core::{convert::TryInto, fmt}; +use core::{convert::TryInto, fmt, sync::atomic::AtomicU16}; use zenoh_keyexpr::{keyexpr, OwnedKeyExpr}; use zenoh_result::{bail, ZResult}; +use crate::network::Mapping; + +/// A numerical Id mapped to a key expression. +pub type ExprId = u16; +pub type ExprLen = u16; + +pub type AtomicExprId = AtomicU16; +pub const EMPTY_EXPR_ID: ExprId = 0; + /// A zenoh **resource** is represented by a pair composed by a **key** and a -/// **value**, such as, ```(/car/telemetry/speed, 320)```. A **resource key** +/// **value**, such as, ```(car/telemetry/speed, 320)```. A **resource key** /// is an arbitrary array of characters, with the exclusion of the symbols /// ```*```, ```**```, ```?```, ```[```, ```]```, and ```#```, /// which have special meaning in the context of zenoh. @@ -51,9 +59,18 @@ use zenoh_result::{bail, ZResult}; pub struct WireExpr<'a> { pub scope: ExprId, // 0 marks global scope pub suffix: Cow<'a, str>, + pub mapping: Mapping, } impl<'a> WireExpr<'a> { + pub fn empty() -> Self { + WireExpr { + scope: 0, + suffix: "".into(), + mapping: Mapping::Sender, + } + } + pub fn as_str(&'a self) -> &'a str { if self.scope == 0 { self.suffix.as_ref() @@ -63,7 +80,7 @@ impl<'a> WireExpr<'a> { } pub fn try_as_str(&'a self) -> ZResult<&'a str> { - if self.scope == 0 { + if self.scope == EMPTY_EXPR_ID { Ok(self.suffix.as_ref()) } else { bail!("Scoped key expression") @@ -94,6 +111,7 @@ impl<'a> WireExpr<'a> { WireExpr { scope: self.scope, suffix: self.suffix.to_string().into(), + mapping: self.mapping, } } @@ -125,11 +143,22 @@ impl TryInto for WireExpr<'_> { } } +impl From for WireExpr<'_> { + fn from(scope: ExprId) -> Self { + Self { + scope, + suffix: "".into(), + mapping: Mapping::Sender, + } + } +} + impl<'a> From<&'a OwnedKeyExpr> for WireExpr<'a> { fn from(val: &'a OwnedKeyExpr) -> Self { WireExpr { scope: 0, suffix: Cow::Borrowed(val.as_str()), + mapping: Mapping::Sender, } } } @@ -139,6 +168,7 @@ impl<'a> From<&'a keyexpr> for WireExpr<'a> { WireExpr { scope: 0, suffix: Cow::Borrowed(val.as_str()), + mapping: Mapping::Sender, } } } @@ -148,7 +178,7 @@ impl fmt::Debug for WireExpr<'_> { if self.scope == 0 { write!(f, "{}", self.suffix) } else { - write!(f, "{}:{}", self.scope, self.suffix) + write!(f, "{}:{:?}:{}", self.scope, self.mapping, self.suffix) } } } @@ -158,7 +188,7 @@ impl fmt::Display for WireExpr<'_> { if self.scope == 0 { write!(f, "{}", self.suffix) } else { - write!(f, "{}:{}", self.scope, self.suffix) + write!(f, "{}:{:?}:{}", self.scope, self.mapping, self.suffix) } } } @@ -170,32 +200,13 @@ impl<'a> From<&WireExpr<'a>> for WireExpr<'a> { } } -impl From for WireExpr<'_> { - #[inline] - fn from(rid: ExprId) -> WireExpr<'static> { - WireExpr { - scope: rid, - suffix: "".into(), - } - } -} - -impl From<&ExprId> for WireExpr<'_> { - #[inline] - fn from(rid: &ExprId) -> WireExpr<'static> { - WireExpr { - scope: *rid, - suffix: "".into(), - } - } -} - impl<'a> From<&'a str> for WireExpr<'a> { #[inline] fn from(name: &'a str) -> WireExpr<'a> { WireExpr { scope: 0, suffix: name.into(), + mapping: Mapping::Sender, } } } @@ -206,6 +217,7 @@ impl From for WireExpr<'_> { WireExpr { scope: 0, suffix: name.into(), + mapping: Mapping::Sender, } } } @@ -216,6 +228,7 @@ impl<'a> From<&'a String> for WireExpr<'a> { WireExpr { scope: 0, suffix: name.into(), + mapping: Mapping::Sender, } } } @@ -244,6 +257,7 @@ impl WireExpr<'_> { WireExpr { scope, suffix: suffix.into(), + mapping: Mapping::default(), } } } diff --git a/commons/zenoh-protocol/src/defaults.rs b/commons/zenoh-protocol/src/defaults.rs deleted file mode 100644 index 326302f5ca..0000000000 --- a/commons/zenoh-protocol/src/defaults.rs +++ /dev/null @@ -1,33 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use super::core::ZInt; - -// Zenoh version -// 7 6 5 4 3 2 1 0 -// +-+-+-+-+-+-+-+-+ -// | v_maj | v_min | -// +-------+-------+ -pub const VERSION: u8 = 0x07; - -// The default sequence number resolution takes 4 bytes on the wire. -// Given the VLE encoding of ZInt, 4 bytes result in 28 useful bits. -// 2^28 = 268_435_456 => Max Seq Num = 268_435_455 -pub const SEQ_NUM_RES: ZInt = 268_435_456; - -/// NOTE: 16 bits (2 bytes) may be prepended to the serialized message indicating the total length -/// in bytes of the message, resulting in the maximum length of a message being 65_535 bytes. -/// This is necessary in those stream-oriented transports (e.g., TCP) that do not preserve -/// the boundary of the serialized messages. The length is encoded as little-endian. -/// In any case, the length of a message must not exceed 65_535 bytes. -pub const BATCH_SIZE: u16 = u16::MAX; diff --git a/commons/zenoh-protocol/src/lib.rs b/commons/zenoh-protocol/src/lib.rs index b0d62afbef..a18aeb766f 100644 --- a/commons/zenoh-protocol/src/lib.rs +++ b/commons/zenoh-protocol/src/lib.rs @@ -22,7 +22,86 @@ extern crate alloc; pub mod common; pub mod core; -pub mod defaults; +pub mod network; pub mod scouting; pub mod transport; pub mod zenoh; + +// Zenoh version +pub const VERSION: u8 = 0x08; + +// Zenoh protocol uses the following conventions for message definition and representation. +// +// +// # Single byte field +// +// A fixed size field of 8 bits. +// +// ```text +// 7 6 5 4 3 2 1 0 +// +-+-+-+-+-+-+-+-+ +// | u8 | +// +---------------+ +// ``` +// +// +// # Variable length field +// +// The field size depends on the element definition and/or actual encoding. An example of variable +// lenght element is an array of bytes (e.g., a payload or a string). +// +// ```text +// 7 6 5 4 3 2 1 0 +// +-+-+-+-+-+-+-+-+ +// ~ element ~ +// +---------------+ +// ``` +// +// +// # u64 field +// +// A u64 is a specialized variable lenght field that is used to encode an unsigned integer. +// +// ```text +// 7 6 5 4 3 2 1 0 +// +-+-+-+-+-+-+-+-+ +// % u64 % +// +---------------+ +// ``` +// +// +// # Array field +// +// An array contains a fixed number of elements whose number is known a priori or indicated by +// another field. Each element can be either a single byte field or a variable legnth field. +// +// ```text +// 7 6 5 4 3 2 1 0 +// +-+-+-+-+-+-+-+-+ +// ~ [element] ~ +// +---------------+ +// ``` +// +// +// # Vector field +// +// A vector contains a variable number of elements and is represented as follows: +// +// ```text +// 7 6 5 4 3 2 1 0 +// +-+-+-+-+-+-+-+-+ +// ~ ~ +// +---------------+ +// ``` +// +// A vector field is always expanded as follows: +// +// ```text +// 7 6 5 4 3 2 1 0 +// +-+-+-+-+-+-+-+-+ +// % num % +// +---------------+ +// ~ [element] ~ +// +---------------+ +// ``` +// diff --git a/commons/zenoh-protocol/src/network/declare.rs b/commons/zenoh-protocol/src/network/declare.rs new file mode 100644 index 0000000000..76415d52f5 --- /dev/null +++ b/commons/zenoh-protocol/src/network/declare.rs @@ -0,0 +1,923 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::{ + common::{imsg, ZExtZ64, ZExtZBuf}, + core::{ExprId, Reliability, WireExpr}, + network::Mapping, + zextz64, zextzbuf, +}; +use alloc::borrow::Cow; +use core::ops::BitOr; +pub use interest::*; +pub use keyexpr::*; +pub use queryable::*; +pub use subscriber::*; +pub use token::*; + +pub mod flag { + // pub const X: u8 = 1 << 5; // 0x20 Reserved + // pub const X: u8 = 1 << 6; // 0x40 Reserved + pub const Z: u8 = 1 << 7; // 0x80 Extensions if Z==1 then an extension will follow +} + +/// Flags: +/// - X: Reserved +/// - X: Reserved +/// - Z: Extension If Z==1 then at least one extension is present +/// +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// |Z|X|X| DECLARE | +/// +-+-+-+---------+ +/// ~ [decl_exts] ~ if Z==1 +/// +---------------+ +/// ~ declaration ~ +/// +---------------+ +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Declare { + pub ext_qos: ext::QoSType, + pub ext_tstamp: Option, + pub ext_nodeid: ext::NodeIdType, + pub body: DeclareBody, +} + +pub mod ext { + use crate::{ + common::{ZExtZ64, ZExtZBuf}, + zextz64, zextzbuf, + }; + + pub type QoS = zextz64!(0x1, false); + pub type QoSType = crate::network::ext::QoSType<{ QoS::ID }>; + + pub type Timestamp = zextzbuf!(0x2, false); + pub type TimestampType = crate::network::ext::TimestampType<{ Timestamp::ID }>; + + pub type NodeId = zextz64!(0x3, true); + pub type NodeIdType = crate::network::ext::NodeIdType<{ NodeId::ID }>; +} + +pub mod id { + pub const D_KEYEXPR: u8 = 0x00; + pub const U_KEYEXPR: u8 = 0x01; + + pub const D_SUBSCRIBER: u8 = 0x02; + pub const U_SUBSCRIBER: u8 = 0x03; + + pub const D_QUERYABLE: u8 = 0x04; + pub const U_QUERYABLE: u8 = 0x05; + + pub const D_TOKEN: u8 = 0x06; + pub const U_TOKEN: u8 = 0x07; + + pub const D_INTEREST: u8 = 0x08; + pub const F_INTEREST: u8 = 0x09; + pub const U_INTEREST: u8 = 0x0A; +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum DeclareBody { + DeclareKeyExpr(DeclareKeyExpr), + UndeclareKeyExpr(UndeclareKeyExpr), + DeclareSubscriber(DeclareSubscriber), + UndeclareSubscriber(UndeclareSubscriber), + DeclareQueryable(DeclareQueryable), + UndeclareQueryable(UndeclareQueryable), + DeclareToken(DeclareToken), + UndeclareToken(UndeclareToken), + DeclareInterest(DeclareInterest), + FinalInterest(FinalInterest), + UndeclareInterest(UndeclareInterest), +} + +impl DeclareBody { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + + let mut rng = rand::thread_rng(); + + match rng.gen_range(0..11) { + 0 => DeclareBody::DeclareKeyExpr(DeclareKeyExpr::rand()), + 1 => DeclareBody::UndeclareKeyExpr(UndeclareKeyExpr::rand()), + 2 => DeclareBody::DeclareSubscriber(DeclareSubscriber::rand()), + 3 => DeclareBody::UndeclareSubscriber(UndeclareSubscriber::rand()), + 4 => DeclareBody::DeclareQueryable(DeclareQueryable::rand()), + 5 => DeclareBody::UndeclareQueryable(UndeclareQueryable::rand()), + 6 => DeclareBody::DeclareToken(DeclareToken::rand()), + 7 => DeclareBody::UndeclareToken(UndeclareToken::rand()), + 8 => DeclareBody::DeclareInterest(DeclareInterest::rand()), + 9 => DeclareBody::FinalInterest(FinalInterest::rand()), + 10 => DeclareBody::UndeclareInterest(UndeclareInterest::rand()), + _ => unreachable!(), + } + } +} + +impl Declare { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + + let mut rng = rand::thread_rng(); + + let body = DeclareBody::rand(); + let ext_qos = ext::QoSType::rand(); + let ext_tstamp = rng.gen_bool(0.5).then(ext::TimestampType::rand); + let ext_nodeid = ext::NodeIdType::rand(); + + Self { + body, + ext_qos, + ext_tstamp, + ext_nodeid, + } + } +} + +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] +#[repr(u8)] +pub enum Mode { + #[default] + Push, + Pull, +} + +impl Mode { + #[cfg(feature = "test")] + fn rand() -> Self { + use rand::Rng; + + let mut rng = rand::thread_rng(); + + if rng.gen_bool(0.5) { + Mode::Push + } else { + Mode::Pull + } + } +} + +pub mod common { + use super::*; + + pub mod ext { + use super::*; + + // WARNING: this is a temporary and mandatory extension used for undeclarations + pub type WireExprExt = zextzbuf!(0x0f, true); + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct WireExprType { + pub wire_expr: WireExpr<'static>, + } + + impl WireExprType { + pub fn null() -> Self { + Self { + wire_expr: WireExpr { + scope: ExprId::MIN, + suffix: Cow::from(""), + mapping: Mapping::Receiver, + }, + } + } + + #[cfg(feature = "test")] + pub fn rand() -> Self { + Self { + wire_expr: WireExpr::rand(), + } + } + } + } +} + +pub mod keyexpr { + use super::*; + + pub mod flag { + pub const N: u8 = 1 << 5; // 0x20 Named if N==1 then the key expr has name/suffix + // pub const X: u8 = 1 << 6; // 0x40 Reserved + pub const Z: u8 = 1 << 7; // 0x80 Extensions if Z==1 then an extension will follow + } + + /// ```text + /// Flags: + /// - N: Named If N==1 then the key expr has name/suffix + /// - X: Reserved + /// - Z: Extension If Z==1 then at least one extension is present + /// + /// 7 6 5 4 3 2 1 0 + /// +-+-+-+-+-+-+-+-+ + /// |Z|X|N| D_KEXPR | + /// +---------------+ + /// ~ expr_id:z16 ~ + /// +---------------+ + /// ~ key_scope:z16 ~ + /// +---------------+ + /// ~ key_suffix ~ if N==1 -- + /// +---------------+ + /// ~ [decl_exts] ~ if Z==1 + /// +---------------+ + /// ``` + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct DeclareKeyExpr { + pub id: ExprId, + pub wire_expr: WireExpr<'static>, + } + + impl DeclareKeyExpr { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); + + let id: ExprId = rng.gen(); + let wire_expr = WireExpr::rand(); + + Self { id, wire_expr } + } + } + + /// ```text + /// Flags: + /// - X: Reserved + /// - X: Reserved + /// - Z: Extension If Z==1 then at least one extension is present + /// + /// 7 6 5 4 3 2 1 0 + /// +-+-+-+-+-+-+-+-+ + /// |Z|X|X| U_KEXPR | + /// +---------------+ + /// ~ expr_id:z16 ~ + /// +---------------+ + /// ~ [decl_exts] ~ if Z==1 + /// +---------------+ + /// ``` + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct UndeclareKeyExpr { + pub id: ExprId, + } + + impl UndeclareKeyExpr { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); + + let id: ExprId = rng.gen(); + + Self { id } + } + } +} + +pub mod subscriber { + use super::*; + + pub type SubscriberId = u32; + + pub mod flag { + pub const N: u8 = 1 << 5; // 0x20 Named if N==1 then the key expr has name/suffix + pub const M: u8 = 1 << 6; // 0x40 Mapping if M==1 then key expr mapping is the one declared by the sender, else it is the one declared by the receiver + pub const Z: u8 = 1 << 7; // 0x80 Extensions if Z==1 then an extension will follow + } + + /// ```text + /// Flags: + /// - N: Named If N==1 then the key expr has name/suffix + /// - M: Mapping if M==1 then key expr mapping is the one declared by the sender, else it is the one declared by the receiver + /// - Z: Extension If Z==1 then at least one extension is present + /// + /// 7 6 5 4 3 2 1 0 + /// +-+-+-+-+-+-+-+-+ + /// |Z|M|N| D_SUB | + /// +---------------+ + /// ~ subs_id:z32 ~ + /// +---------------+ + /// ~ key_scope:z16 ~ + /// +---------------+ + /// ~ key_suffix ~ if N==1 -- + /// +---------------+ + /// ~ [decl_exts] ~ if Z==1 + /// +---------------+ + /// + /// - if R==1 then the subscription is reliable, else it is best effort + /// - if P==1 then the subscription is pull, else it is push + /// + /// ``` + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct DeclareSubscriber { + pub id: SubscriberId, + pub wire_expr: WireExpr<'static>, + pub ext_info: ext::SubscriberInfo, + } + + pub mod ext { + use super::*; + + pub type Info = zextz64!(0x01, false); + + /// # The subscription mode. + /// + /// ```text + /// 7 6 5 4 3 2 1 0 + /// +-+-+-+-+-+-+-+-+ + /// |Z|0_1| ID | + /// +-+-+-+---------+ + /// % reserved |P|R% + /// +---------------+ + /// + /// - if R==1 then the subscription is reliable, else it is best effort + /// - if P==1 then the subscription is pull, else it is push + /// - rsv: Reserved + /// ``` + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] + pub struct SubscriberInfo { + pub reliability: Reliability, + pub mode: Mode, + } + + impl SubscriberInfo { + pub const R: u64 = 1; + pub const P: u64 = 1 << 1; + + #[cfg(feature = "test")] + pub fn rand() -> Self { + let reliability = Reliability::rand(); + let mode = Mode::rand(); + + Self { reliability, mode } + } + } + + impl From for SubscriberInfo { + fn from(ext: Info) -> Self { + let reliability = if imsg::has_option(ext.value, SubscriberInfo::R) { + Reliability::Reliable + } else { + Reliability::BestEffort + }; + let mode = if imsg::has_option(ext.value, SubscriberInfo::P) { + Mode::Pull + } else { + Mode::Push + }; + Self { reliability, mode } + } + } + + impl From for Info { + fn from(ext: SubscriberInfo) -> Self { + let mut v: u64 = 0; + if ext.reliability == Reliability::Reliable { + v |= SubscriberInfo::R; + } + if ext.mode == Mode::Pull { + v |= SubscriberInfo::P; + } + Info::new(v) + } + } + } + + impl DeclareSubscriber { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); + + let id: SubscriberId = rng.gen(); + let wire_expr = WireExpr::rand(); + let ext_info = ext::SubscriberInfo::rand(); + + Self { + id, + wire_expr, + ext_info, + } + } + } + + /// ```text + /// Flags: + /// - X: Reserved + /// - X: Reserved + /// - Z: Extension If Z==1 then at least one extension is present + /// + /// 7 6 5 4 3 2 1 0 + /// +-+-+-+-+-+-+-+-+ + /// |Z|X|X| U_SUB | + /// +---------------+ + /// ~ subs_id:z32 ~ + /// +---------------+ + /// ~ [decl_exts] ~ if Z==1 + /// +---------------+ + /// ``` + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct UndeclareSubscriber { + pub id: SubscriberId, + // WARNING: this is a temporary and mandatory extension used for undeclarations + pub ext_wire_expr: common::ext::WireExprType, + } + + impl UndeclareSubscriber { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); + + let id: SubscriberId = rng.gen(); + let ext_wire_expr = common::ext::WireExprType::rand(); + + Self { id, ext_wire_expr } + } + } +} + +pub mod queryable { + use super::*; + + pub type QueryableId = u32; + + pub mod flag { + pub const N: u8 = 1 << 5; // 0x20 Named if N==1 then the key expr has name/suffix + pub const M: u8 = 1 << 6; // 0x40 Mapping if M==1 then key expr mapping is the one declared by the sender, else it is the one declared by the receiver + pub const Z: u8 = 1 << 7; // 0x80 Extensions if Z==1 then an extension will follow + } + + /// ```text + /// Flags: + /// - N: Named If N==1 then the key expr has name/suffix + /// - M: Mapping if M==1 then key expr mapping is the one declared by the sender, else it is the one declared by the receiver + /// - Z: Extension If Z==1 then at least one extension is present + /// + /// 7 6 5 4 3 2 1 0 + /// +-+-+-+-+-+-+-+-+ + /// |Z|M|N| D_QBL | + /// +---------------+ + /// ~ qbls_id:z32 ~ + /// +---------------+ + /// ~ key_scope:z16 ~ + /// +---------------+ + /// ~ key_suffix ~ if N==1 -- + /// +---------------+ + /// ~ [decl_exts] ~ if Z==1 + /// +---------------+ + /// + /// - if R==1 then the queryable is reliable, else it is best effort + /// - if P==1 then the queryable is pull, else it is push + /// - if C==1 then the queryable is complete and the N parameter is present + /// - if D==1 then the queryable distance is present + /// ``` + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct DeclareQueryable { + pub id: QueryableId, + pub wire_expr: WireExpr<'static>, + pub ext_info: ext::QueryableInfo, + } + + pub mod ext { + use super::*; + + pub type Info = zextz64!(0x01, false); + + /// 7 6 5 4 3 2 1 0 + /// +-+-+-+-+-+-+-+-+ + /// |Z|0_1| ID | + /// +-+-+-+---------+ + /// ~ complete_n ~ + /// +---------------+ + /// ~ distance ~ + /// +---------------+ + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] + pub struct QueryableInfo { + pub complete: u8, // Default 0: incomplete // @TODO: maybe a bitflag + pub distance: u32, // Default 0: no distance + } + + impl QueryableInfo { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); + let complete: u8 = rng.gen(); + let distance: u32 = rng.gen(); + + Self { complete, distance } + } + } + + impl From for QueryableInfo { + fn from(ext: Info) -> Self { + let complete = ext.value as u8; + let distance = (ext.value >> 8) as u32; + + Self { complete, distance } + } + } + + impl From for Info { + fn from(ext: QueryableInfo) -> Self { + let mut v: u64 = ext.complete as u64; + v |= (ext.distance as u64) << 8; + Info::new(v) + } + } + } + + impl DeclareQueryable { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); + + let id: QueryableId = rng.gen(); + let wire_expr = WireExpr::rand(); + let ext_info = ext::QueryableInfo::rand(); + + Self { + id, + wire_expr, + ext_info, + } + } + } + + /// ```text + /// Flags: + /// - X: Reserved + /// - X: Reserved + /// - Z: Extension If Z==1 then at least one extension is present + /// + /// 7 6 5 4 3 2 1 0 + /// +-+-+-+-+-+-+-+-+ + /// |Z|X|X| U_QBL | + /// +---------------+ + /// ~ qbls_id:z32 ~ + /// +---------------+ + /// ~ [decl_exts] ~ if Z==1 + /// +---------------+ + /// ``` + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct UndeclareQueryable { + pub id: QueryableId, + // WARNING: this is a temporary and mandatory extension used for undeclarations + pub ext_wire_expr: common::ext::WireExprType, + } + + impl UndeclareQueryable { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); + + let id: QueryableId = rng.gen(); + let ext_wire_expr = common::ext::WireExprType::rand(); + + Self { id, ext_wire_expr } + } + } +} + +pub mod token { + use super::*; + + pub type TokenId = u32; + + pub mod flag { + pub const N: u8 = 1 << 5; // 0x20 Named if N==1 then the key expr has name/suffix + pub const M: u8 = 1 << 6; // 0x40 Mapping if M==1 then key expr mapping is the one declared by the sender, else it is the one declared by the receiver + pub const Z: u8 = 1 << 7; // 0x80 Extensions if Z==1 then an extension will follow + } + + /// ```text + /// Flags: + /// - N: Named If N==1 then the key expr has name/suffix + /// - M: Mapping if M==1 then key expr mapping is the one declared by the sender, else it is the one declared by the receiver + /// - Z: Extension If Z==1 then at least one extension is present + /// + /// 7 6 5 4 3 2 1 0 + /// +-+-+-+-+-+-+-+-+ + /// |Z|M|N| D_TKN | + /// +---------------+ + /// ~ token_id:z32 ~ + /// +---------------+ + /// ~ key_scope:z16 ~ + /// +---------------+ + /// ~ key_suffix ~ if N==1 -- + /// +---------------+ + /// ~ [decl_exts] ~ if Z==1 + /// +---------------+ + /// + /// ``` + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct DeclareToken { + pub id: TokenId, + pub wire_expr: WireExpr<'static>, + } + + impl DeclareToken { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); + + let id: TokenId = rng.gen(); + let wire_expr = WireExpr::rand(); + + Self { id, wire_expr } + } + } + + /// ```text + /// Flags: + /// - X: Reserved + /// - X: Reserved + /// - Z: Extension If Z==1 then at least one extension is present + /// + /// 7 6 5 4 3 2 1 0 + /// +-+-+-+-+-+-+-+-+ + /// |Z|X|X| U_TKN | + /// +---------------+ + /// ~ token_id:z32 ~ + /// +---------------+ + /// ~ [decl_exts] ~ if Z==1 + /// +---------------+ + /// ``` + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct UndeclareToken { + pub id: TokenId, + // WARNING: this is a temporary and mandatory extension used for undeclarations + pub ext_wire_expr: common::ext::WireExprType, + } + + impl UndeclareToken { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); + + let id: TokenId = rng.gen(); + let ext_wire_expr = common::ext::WireExprType::rand(); + + Self { id, ext_wire_expr } + } + } +} + +pub mod interest { + use super::*; + + pub type InterestId = u32; + + pub mod flag { + pub const N: u8 = 1 << 5; // 0x20 Named if N==1 then the key expr has name/suffix + pub const M: u8 = 1 << 6; // 0x40 Mapping if M==1 then key expr mapping is the one declared by the sender, else it is the one declared by the receiver + pub const Z: u8 = 1 << 7; // 0x80 Extensions if Z==1 then an extension will follow + } + + /// # DeclareInterest message + /// + /// The DECLARE INTEREST message is sent to request the transmission of existing and future + /// declarations of a given kind matching a target keyexpr. E.g., a declare interest could be sent to + /// request the transmisison of all existing subscriptions matching `a/*`. A FINAL INTEREST is used to + /// mark the end of the transmission of exisiting matching declarations. + /// + /// E.g., the [`DeclareInterest`]/[`FinalInterest`]/[`UndeclareInterest`] message flow is the following: + /// + /// ```text + /// A B + /// | DECL INTEREST | + /// |------------------>| -- This is a DeclareInterest e.g. for subscriber declarations/undeclarations. + /// | | + /// | DECL SUBSCRIBER | + /// |<------------------| + /// | DECL SUBSCRIBER | + /// |<------------------| + /// | DECL SUBSCRIBER | + /// |<------------------| + /// | | + /// | FINAL INTEREST | + /// |<------------------| -- The FinalInterest signals that all known subscribers have been transmitted. + /// | | + /// | DECL SUBSCRIBER | + /// |<------------------| -- This is a new subscriber declaration. + /// | UNDECL SUBSCRIBER | + /// |<------------------| -- This is a new subscriber undeclaration. + /// | | + /// | ... | + /// | | + /// | UNDECL INTEREST | + /// |------------------>| -- This is an UndeclareInterest to stop receiving subscriber declarations/undeclarations. + /// | | + /// ``` + /// + /// The DECLARE INTEREST message structure is defined as follows: + /// + /// ```text + /// Flags: + /// - N: Named If N==1 then the key expr has name/suffix + /// - M: Mapping if M==1 then key expr mapping is the one declared by the sender, else it is the one declared by the receiver + /// - Z: Extension If Z==1 then at least one extension is present + /// + /// 7 6 5 4 3 2 1 0 + /// +-+-+-+-+-+-+-+-+ + /// |Z|M|N| D_INT | + /// +---------------+ + /// ~ intst_id:z32 ~ + /// +---------------+ + /// ~ key_scope:z16 ~ + /// +---------------+ + /// ~ key_suffix ~ if N==1 -- + /// +---------------+ + /// |A|F|C|X|T|Q|S|K| (*) + /// +---------------+ + /// ~ [decl_exts] ~ if Z==1 + /// +---------------+ + /// + /// (*) - if K==1 then the interest refers to key expressions + /// - if S==1 then the interest refers to subscribers + /// - if Q==1 then the interest refers to queryables + /// - if T==1 then the interest refers to tokens + /// - if C==1 then the interest refers to the current declarations. + /// - if F==1 then the interest refers to the future declarations. Note that if F==0 then: + /// - replies SHOULD NOT be sent after the FinalInterest; + /// - UndeclareInterest SHOULD NOT be sent after the FinalInterest. + /// - if A==1 then the replies SHOULD be aggregated + /// ``` + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct DeclareInterest { + pub id: InterestId, + pub wire_expr: WireExpr<'static>, + pub interest: Interest, + } + + #[repr(transparent)] + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Interest(u8); + + impl Interest { + pub const KEYEXPRS: Interest = Interest(1); + pub const SUBSCRIBERS: Interest = Interest(1 << 1); + pub const QUERYABLES: Interest = Interest(1 << 2); + pub const TOKENS: Interest = Interest(1 << 3); + // pub const X: Interest = Interest(1 << 4); + pub const CURRENT: Interest = Interest(1 << 5); + pub const FUTURE: Interest = Interest(1 << 6); + pub const AGGREGATE: Interest = Interest(1 << 7); + + pub const fn keyexprs(&self) -> bool { + imsg::has_flag(self.0, Self::KEYEXPRS.0) + } + + pub const fn subscribers(&self) -> bool { + imsg::has_flag(self.0, Self::SUBSCRIBERS.0) + } + + pub const fn queryables(&self) -> bool { + imsg::has_flag(self.0, Self::QUERYABLES.0) + } + + pub const fn tokens(&self) -> bool { + imsg::has_flag(self.0, Self::TOKENS.0) + } + + pub const fn current(&self) -> bool { + imsg::has_flag(self.0, Self::CURRENT.0) + } + + pub const fn future(&self) -> bool { + imsg::has_flag(self.0, Self::FUTURE.0) + } + + pub const fn aggregate(&self) -> bool { + imsg::has_flag(self.0, Self::AGGREGATE.0) + } + + pub const fn as_u8(&self) -> u8 { + self.0 + } + + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); + + let inner: u8 = rng.gen(); + + Self(inner) + } + } + + impl BitOr for Interest { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) + } + } + + impl From for Interest { + fn from(v: u8) -> Self { + Self(v) + } + } + + impl DeclareInterest { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); + + let id: InterestId = rng.gen(); + let wire_expr = WireExpr::rand(); + let interest = Interest::rand(); + + Self { + id, + wire_expr, + interest, + } + } + } + + /// ```text + /// Flags: + /// - X: Reserved + /// - X: Reserved + /// - Z: Extension If Z==1 then at least one extension is present + /// + /// 7 6 5 4 3 2 1 0 + /// +-+-+-+-+-+-+-+-+ + /// |Z|X|X| F_INT | + /// +---------------+ + /// ~ intst_id:z32 ~ + /// +---------------+ + /// ~ [decl_exts] ~ if Z==1 + /// +---------------+ + /// ``` + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct FinalInterest { + pub id: InterestId, + } + + impl FinalInterest { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); + + let id: InterestId = rng.gen(); + + Self { id } + } + } + + /// ```text + /// Flags: + /// - X: Reserved + /// - X: Reserved + /// - Z: Extension If Z==1 then at least one extension is present + /// + /// 7 6 5 4 3 2 1 0 + /// +-+-+-+-+-+-+-+-+ + /// |Z|X|X| U_INT | + /// +---------------+ + /// ~ intst_id:z32 ~ + /// +---------------+ + /// ~ [decl_exts] ~ if Z==1 + /// +---------------+ + /// ``` + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct UndeclareInterest { + pub id: InterestId, + // WARNING: this is a temporary and mandatory extension used for undeclarations + pub ext_wire_expr: common::ext::WireExprType, + } + + impl UndeclareInterest { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); + + let id: InterestId = rng.gen(); + let ext_wire_expr = common::ext::WireExprType::rand(); + + Self { id, ext_wire_expr } + } + } +} diff --git a/commons/zenoh-protocol/src/network/mod.rs b/commons/zenoh-protocol/src/network/mod.rs new file mode 100644 index 0000000000..44464c4b13 --- /dev/null +++ b/commons/zenoh-protocol/src/network/mod.rs @@ -0,0 +1,421 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +pub mod declare; +pub mod oam; +pub mod push; +pub mod request; +pub mod response; + +use core::fmt; + +pub use declare::{ + Declare, DeclareBody, DeclareInterest, DeclareKeyExpr, DeclareQueryable, DeclareSubscriber, + DeclareToken, UndeclareInterest, UndeclareKeyExpr, UndeclareQueryable, UndeclareSubscriber, + UndeclareToken, +}; +pub use oam::Oam; +pub use push::Push; +pub use request::{AtomicRequestId, Request, RequestId}; +pub use response::{Response, ResponseFinal}; + +use crate::core::{CongestionControl, Priority}; + +pub mod id { + // WARNING: it's crucial that these IDs do NOT collide with the IDs + // defined in `crate::transport::id`. + pub const OAM: u8 = 0x1f; + pub const DECLARE: u8 = 0x1e; + pub const PUSH: u8 = 0x1d; + pub const REQUEST: u8 = 0x1c; + pub const RESPONSE: u8 = 0x1b; + pub const RESPONSE_FINAL: u8 = 0x1a; +} + +#[repr(u8)] +#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)] +pub enum Mapping { + #[default] + Receiver = 0, + Sender = 1, +} + +impl Mapping { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + + let mut rng = rand::thread_rng(); + if rng.gen_bool(0.5) { + Mapping::Sender + } else { + Mapping::Receiver + } + } +} + +// Zenoh messages at zenoh-network level +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum NetworkBody { + Push(Push), + Request(Request), + Response(Response), + ResponseFinal(ResponseFinal), + Declare(Declare), + OAM(Oam), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct NetworkMessage { + pub body: NetworkBody, + #[cfg(feature = "stats")] + pub size: Option, +} + +impl NetworkMessage { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + + let mut rng = rand::thread_rng(); + + let body = match rng.gen_range(0..6) { + 0 => NetworkBody::Push(Push::rand()), + 1 => NetworkBody::Request(Request::rand()), + 2 => NetworkBody::Response(Response::rand()), + 3 => NetworkBody::ResponseFinal(ResponseFinal::rand()), + 4 => NetworkBody::Declare(Declare::rand()), + 5 => NetworkBody::OAM(Oam::rand()), + _ => unreachable!(), + }; + + body.into() + } + + #[inline] + pub fn is_reliable(&self) -> bool { + // TODO + true + } + + #[inline] + pub fn is_droppable(&self) -> bool { + if !self.is_reliable() { + return true; + } + + let cc = match &self.body { + NetworkBody::Declare(msg) => msg.ext_qos.get_congestion_control(), + NetworkBody::Push(msg) => msg.ext_qos.get_congestion_control(), + NetworkBody::Request(msg) => msg.ext_qos.get_congestion_control(), + NetworkBody::Response(msg) => msg.ext_qos.get_congestion_control(), + NetworkBody::ResponseFinal(msg) => msg.ext_qos.get_congestion_control(), + NetworkBody::OAM(msg) => msg.ext_qos.get_congestion_control(), + }; + + cc == CongestionControl::Drop + } + + #[inline] + pub fn priority(&self) -> Priority { + match &self.body { + NetworkBody::Declare(msg) => msg.ext_qos.get_priority(), + NetworkBody::Push(msg) => msg.ext_qos.get_priority(), + NetworkBody::Request(msg) => msg.ext_qos.get_priority(), + NetworkBody::Response(msg) => msg.ext_qos.get_priority(), + NetworkBody::ResponseFinal(msg) => msg.ext_qos.get_priority(), + NetworkBody::OAM(msg) => msg.ext_qos.get_priority(), + } + } +} + +impl fmt::Display for NetworkMessage { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(self, f) + } +} + +impl From for NetworkMessage { + #[inline] + fn from(body: NetworkBody) -> Self { + Self { + body, + #[cfg(feature = "stats")] + size: None, + } + } +} + +impl From for NetworkMessage { + fn from(declare: Declare) -> Self { + NetworkBody::Declare(declare).into() + } +} + +impl From for NetworkMessage { + fn from(push: Push) -> Self { + NetworkBody::Push(push).into() + } +} + +impl From for NetworkMessage { + fn from(request: Request) -> Self { + NetworkBody::Request(request).into() + } +} + +impl From for NetworkMessage { + fn from(response: Response) -> Self { + NetworkBody::Response(response).into() + } +} + +impl From for NetworkMessage { + fn from(final_response: ResponseFinal) -> Self { + NetworkBody::ResponseFinal(final_response).into() + } +} + +// Extensions +pub mod ext { + use crate::{ + common::{imsg, ZExtZ64}, + core::{CongestionControl, Priority, ZenohId}, + }; + use core::fmt; + + /// ```text + /// 7 6 5 4 3 2 1 0 + /// +-+-+-+-+-+-+-+-+ + /// |Z|0_1| ID | + /// +-+-+-+---------+ + /// %0|rsv|E|D|prio % + /// +---------------+ + /// + /// - prio: Priority class + /// - D: Don't drop. Don't drop the message for congestion control. + /// - E: Express. Don't batch this message. + /// - rsv: Reserved + /// ``` + #[repr(transparent)] + #[derive(Clone, Copy, PartialEq, Eq)] + pub struct QoSType { + inner: u8, + } + + impl QoSType<{ ID }> { + const P_MASK: u8 = 0b00000111; + const D_FLAG: u8 = 0b00001000; + const E_FLAG: u8 = 0b00010000; + + pub const fn new( + priority: Priority, + congestion_control: CongestionControl, + is_express: bool, + ) -> Self { + let mut inner = priority as u8; + if let CongestionControl::Block = congestion_control { + inner |= Self::D_FLAG; + } + if is_express { + inner |= Self::E_FLAG; + } + Self { inner } + } + + pub fn set_priority(&mut self, priority: Priority) { + self.inner = imsg::set_flag(self.inner, priority as u8); + } + + pub const fn get_priority(&self) -> Priority { + unsafe { core::mem::transmute(self.inner & Self::P_MASK) } + } + + pub fn set_congestion_control(&mut self, cctrl: CongestionControl) { + match cctrl { + CongestionControl::Block => self.inner = imsg::set_flag(self.inner, Self::D_FLAG), + CongestionControl::Drop => self.inner = imsg::unset_flag(self.inner, Self::D_FLAG), + } + } + + pub const fn get_congestion_control(&self) -> CongestionControl { + match imsg::has_flag(self.inner, Self::D_FLAG) { + true => CongestionControl::Block, + false => CongestionControl::Drop, + } + } + + pub const fn is_express(&self) -> bool { + imsg::has_flag(self.inner, Self::E_FLAG) + } + + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); + + let inner: u8 = rng.gen(); + Self { inner } + } + + pub fn declare_default() -> Self { + Self::new(Priority::default(), CongestionControl::Block, false) + } + + pub fn push_default() -> Self { + Self::new(Priority::default(), CongestionControl::Drop, false) + } + + pub fn request_default() -> Self { + Self::new(Priority::default(), CongestionControl::Block, false) + } + + pub fn response_default() -> Self { + Self::new(Priority::default(), CongestionControl::Block, false) + } + + pub fn response_final_default() -> Self { + Self::new(Priority::default(), CongestionControl::Block, false) + } + + pub fn oam_default() -> Self { + Self::new(Priority::default(), CongestionControl::Block, false) + } + } + + impl Default for QoSType<{ ID }> { + fn default() -> Self { + Self::new(Priority::default(), CongestionControl::default(), false) + } + } + + impl From> for QoSType<{ ID }> { + fn from(ext: ZExtZ64<{ ID }>) -> Self { + Self { + inner: ext.value as u8, + } + } + } + + impl From> for ZExtZ64<{ ID }> { + fn from(ext: QoSType<{ ID }>) -> Self { + ZExtZ64::new(ext.inner as u64) + } + } + + impl fmt::Debug for QoSType<{ ID }> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("QoS") + .field("priority", &self.get_priority()) + .field("congestion", &self.get_congestion_control()) + .field("express", &self.is_express()) + .finish() + } + } + + /// ```text + /// 7 6 5 4 3 2 1 0 + /// +-+-+-+-+-+-+-+-+ + /// |Z|1_0| ID | + /// +-+-+-+---------+ + /// ~ ts: ~ + /// +---------------+ + /// ``` + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub struct TimestampType { + pub timestamp: uhlc::Timestamp, + } + + impl TimestampType<{ ID }> { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); + + let time = uhlc::NTP64(rng.gen()); + let id = uhlc::ID::try_from(ZenohId::rand().to_le_bytes()).unwrap(); + let timestamp = uhlc::Timestamp::new(time, id); + Self { timestamp } + } + } + + /// ```text + /// 7 6 5 4 3 2 1 0 + /// +-+-+-+-+-+-+-+-+ + /// |Z|0_1| ID | + /// +-+-+-+---------+ + /// % node_id % + /// +---------------+ + /// ``` + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub struct NodeIdType { + pub node_id: u16, + } + + impl NodeIdType<{ ID }> { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); + let node_id = rng.gen(); + Self { node_id } + } + } + + impl Default for NodeIdType<{ ID }> { + fn default() -> Self { + // node_id == 0 means the message has been generated by the node itself + Self { node_id: 0 } + } + } + + impl From> for NodeIdType<{ ID }> { + fn from(ext: ZExtZ64<{ ID }>) -> Self { + Self { + node_id: ext.value as u16, + } + } + } + + impl From> for ZExtZ64<{ ID }> { + fn from(ext: NodeIdType<{ ID }>) -> Self { + ZExtZ64::new(ext.node_id as u64) + } + } + + /// 7 6 5 4 3 2 1 0 + /// +-+-+-+-+-+-+-+-+ + /// |zid_len|X|X|X|X| + /// +-------+-+-+---+ + /// ~ zid ~ + /// +---------------+ + /// % eid % + /// +---------------+ + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct EntityIdType { + pub zid: ZenohId, + pub eid: u32, + } + + impl EntityIdType<{ ID }> { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); + + let zid = ZenohId::rand(); + let eid: u32 = rng.gen(); + Self { zid, eid } + } + } +} diff --git a/commons/zenoh-protocol/src/network/oam.rs b/commons/zenoh-protocol/src/network/oam.rs new file mode 100644 index 0000000000..814257c1e1 --- /dev/null +++ b/commons/zenoh-protocol/src/network/oam.rs @@ -0,0 +1,92 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::common::ZExtBody; + +pub type OamId = u16; + +pub mod flag { + pub const T: u8 = 1 << 5; // 0x20 Transport + // pub const X: u8 = 1 << 6; // 0x40 Reserved + pub const Z: u8 = 1 << 7; // 0x80 Extensions if Z==1 then an extension will follow +} + +pub mod id { + use super::OamId; + + pub const OAM_LINKSTATE: OamId = 0x0001; +} + +/// ```text +/// Flags: +/// - E |: Encoding The encoding of the extension +/// - E/ +/// - Z: Extension If Z==1 then at least one extension is present +/// +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// |X|ENC| OAM | +/// +-+-+-+---------+ +/// ~ id:z16 ~ +/// +---------------+ +/// % length % -- If ENC == Z64 || ENC == ZBuf (z32) +/// +---------------+ +/// ~ [u8] ~ -- If ENC == ZBuf +/// +---------------+ +/// +/// Encoding: +/// - 0b00: Unit +/// - 0b01: Z64 +/// - 0b10: ZBuf +/// - 0b11: Reserved +/// ``` +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Oam { + pub id: OamId, + pub body: ZExtBody, + pub ext_qos: ext::QoSType, + pub ext_tstamp: Option, +} + +pub mod ext { + use crate::{ + common::{ZExtZ64, ZExtZBuf}, + zextz64, zextzbuf, + }; + + pub type QoS = zextz64!(0x1, false); + pub type QoSType = crate::network::ext::QoSType<{ QoS::ID }>; + + pub type Timestamp = zextzbuf!(0x2, false); + pub type TimestampType = crate::network::ext::TimestampType<{ Timestamp::ID }>; +} + +impl Oam { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); + + let id: OamId = rng.gen(); + let body = ZExtBody::rand(); + let ext_qos = ext::QoSType::rand(); + let ext_tstamp = rng.gen_bool(0.5).then(ext::TimestampType::rand); + + Self { + id, + body, + ext_qos, + ext_tstamp, + } + } +} diff --git a/commons/zenoh-protocol/src/network/push.rs b/commons/zenoh-protocol/src/network/push.rs new file mode 100644 index 0000000000..98a773b147 --- /dev/null +++ b/commons/zenoh-protocol/src/network/push.rs @@ -0,0 +1,86 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::{core::WireExpr, zenoh::PushBody}; + +pub mod flag { + pub const N: u8 = 1 << 5; // 0x20 Named if N==1 then the key expr has name/suffix + pub const M: u8 = 1 << 6; // 0x40 Mapping if M==1 then key expr mapping is the one declared by the sender, else it is the one declared by the receiver + pub const Z: u8 = 1 << 7; // 0x80 Extensions if Z==1 then an extension will follow +} + +/// ```text +/// Flags: +/// - N: Named If N==1 then the key expr has name/suffix +/// - M: Mapping if M==1 then key expr mapping is the one declared by the sender, else it is the one declared by the receiver +/// - Z: Extension If Z==1 then at least one extension is present +/// +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// |Z|M|N| PUSH | +/// +-+-+-+---------+ +/// ~ key_scope:z16 ~ +/// +---------------+ +/// ~ key_suffix ~ if N==1 -- +/// +---------------+ +/// ~ [push_exts] ~ if Z==1 +/// +---------------+ +/// ~ PushBody ~ +/// +---------------+ +/// ``` +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Push { + pub wire_expr: WireExpr<'static>, + pub ext_qos: ext::QoSType, + pub ext_tstamp: Option, + pub ext_nodeid: ext::NodeIdType, + pub payload: PushBody, +} + +pub mod ext { + use crate::{ + common::{ZExtZ64, ZExtZBuf}, + zextz64, zextzbuf, + }; + + pub type QoS = zextz64!(0x1, false); + pub type QoSType = crate::network::ext::QoSType<{ QoS::ID }>; + + pub type Timestamp = zextzbuf!(0x2, false); + pub type TimestampType = crate::network::ext::TimestampType<{ Timestamp::ID }>; + + pub type NodeId = zextz64!(0x3, true); + pub type NodeIdType = crate::network::ext::NodeIdType<{ NodeId::ID }>; +} + +impl Push { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + + let mut rng = rand::thread_rng(); + let wire_expr = WireExpr::rand(); + let payload = PushBody::rand(); + let ext_qos = ext::QoSType::rand(); + let ext_tstamp = rng.gen_bool(0.5).then(ext::TimestampType::rand); + let ext_nodeid = ext::NodeIdType::rand(); + + Self { + wire_expr, + payload, + ext_tstamp, + ext_qos, + ext_nodeid, + } + } +} diff --git a/commons/zenoh-protocol/src/network/request.rs b/commons/zenoh-protocol/src/network/request.rs new file mode 100644 index 0000000000..17bab1905d --- /dev/null +++ b/commons/zenoh-protocol/src/network/request.rs @@ -0,0 +1,158 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::{core::WireExpr, zenoh::RequestBody}; +use core::sync::atomic::AtomicU32; + +/// The resolution of a RequestId +pub type RequestId = u32; +pub type AtomicRequestId = AtomicU32; + +pub mod flag { + pub const N: u8 = 1 << 5; // 0x20 Named if N==1 then the key expr has name/suffix + pub const M: u8 = 1 << 6; // 0x40 Mapping if M==1 then key expr mapping is the one declared by the sender, else it is the one declared by the receiver + pub const Z: u8 = 1 << 7; // 0x80 Extensions if Z==1 then an extension will follow +} + +/// # Request message +/// +/// ```text +/// Flags: +/// - N: Named if N==1 then the key expr has name/suffix +/// - M: Mapping if M==1 then key expr mapping is the one declared by the sender, else it is the one declared by the receiver +/// - Z: Extension if Z==1 then at least one extension is present +/// +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// |Z|M|N| Request | +/// +-+-+-+---------+ +/// ~ request_id:z32~ (*) +/// +---------------+ +/// ~ key_scope:z16 ~ +/// +---------------+ +/// ~ key_suffix ~ if N==1 -- +/// +---------------+ +/// ~ [req_exts] ~ if Z==1 +/// +---------------+ +/// ~ RequestBody ~ -- Payload +/// +---------------+ +/// +/// (*) The resolution of the request id is negotiated during the session establishment. +/// This implementation limits the resolution to 32bit. +/// ``` +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Request { + pub id: RequestId, + pub wire_expr: WireExpr<'static>, + pub ext_qos: ext::QoSType, + pub ext_tstamp: Option, + pub ext_nodeid: ext::NodeIdType, + pub ext_target: ext::TargetType, + pub ext_budget: Option, + pub ext_timeout: Option, + pub payload: RequestBody, +} + +pub mod ext { + use crate::{ + common::{ZExtZ64, ZExtZBuf}, + core::QueryTarget, + zextz64, zextzbuf, + }; + use core::{num::NonZeroU32, time::Duration}; + + pub type QoS = zextz64!(0x1, false); + pub type QoSType = crate::network::ext::QoSType<{ QoS::ID }>; + + pub type Timestamp = zextzbuf!(0x2, false); + pub type TimestampType = crate::network::ext::TimestampType<{ Timestamp::ID }>; + + pub type NodeId = zextz64!(0x3, true); + pub type NodeIdType = crate::network::ext::NodeIdType<{ NodeId::ID }>; + + pub type Target = zextz64!(0x4, true); + /// - Target (0x03) + /// 7 6 5 4 3 2 1 0 + /// +-+-+-+-+-+-+-+-+ + /// % target % + /// +---------------+ + /// + /// The `zenoh::queryable::Queryable`s that should be target of a `zenoh::Session::get()`. + pub type TargetType = QueryTarget; + + impl TargetType { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::prelude::SliceRandom; + let mut rng = rand::thread_rng(); + + *[ + TargetType::All, + TargetType::AllComplete, + TargetType::BestMatching, + #[cfg(feature = "complete_n")] + TargetType::Complete(rng.gen()), + ] + .choose(&mut rng) + .unwrap() + } + } + + // The maximum number of responses + pub type Budget = zextz64!(0x5, false); + pub type BudgetType = NonZeroU32; + + // The timeout of the request + pub type Timeout = zextz64!(0x6, false); + pub type TimeoutType = Duration; +} + +impl Request { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use core::num::NonZeroU32; + + use rand::Rng; + + let mut rng = rand::thread_rng(); + let wire_expr = WireExpr::rand(); + let id: RequestId = rng.gen(); + let payload = RequestBody::rand(); + let ext_qos = ext::QoSType::rand(); + let ext_tstamp = rng.gen_bool(0.5).then(ext::TimestampType::rand); + let ext_nodeid = ext::NodeIdType::rand(); + let ext_target = ext::TargetType::rand(); + let ext_budget = if rng.gen_bool(0.5) { + NonZeroU32::new(rng.gen()) + } else { + None + }; + let ext_timeout = if rng.gen_bool(0.5) { + Some(ext::TimeoutType::from_millis(rng.gen())) + } else { + None + }; + + Self { + wire_expr, + id, + payload, + ext_qos, + ext_tstamp, + ext_nodeid, + ext_target, + ext_budget, + ext_timeout, + } + } +} diff --git a/commons/zenoh-protocol/src/network/response.rs b/commons/zenoh-protocol/src/network/response.rs new file mode 100644 index 0000000000..9ef2c26a10 --- /dev/null +++ b/commons/zenoh-protocol/src/network/response.rs @@ -0,0 +1,139 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::{core::WireExpr, network::RequestId, zenoh::ResponseBody}; + +/// # Response message +/// +/// ```text +/// Flags: +/// - N: Named If N==1 then the key expr has name/suffix +/// - M: Mapping if M==1 then key expr mapping is the one declared by the sender, else it is the one declared by the receiver +/// - Z: Extension If Z==1 then at least one extension is present +/// +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// |Z|M|N| Response| +/// +-+-+-+---------+ +/// ~ request_id:z32~ (*) +/// +---------------+ +/// ~ key_scope:z16 ~ +/// +---------------+ +/// ~ key_suffix ~ if N==1 -- +/// +---------------+ +/// ~ [reply_exts] ~ if Z==1 +/// +---------------+ +/// ~ ResponseBody ~ -- Payload +/// +---------------+ +/// +/// (*) The resolution of the request id is negotiated during the session establishment. +/// This implementation limits the resolution to 32bit. +/// ``` +pub mod flag { + pub const N: u8 = 1 << 5; // 0x20 Named if N==1 then the key expr has name/suffix + pub const M: u8 = 1 << 6; // 0x40 Mapping if M==1 then key expr mapping is the one declared by the sender, else it is the one declared by the receiver + pub const Z: u8 = 1 << 7; // 0x80 Extensions if Z==1 then an extension will follow +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Response { + pub rid: RequestId, + pub wire_expr: WireExpr<'static>, + pub payload: ResponseBody, + pub ext_qos: ext::QoSType, + pub ext_tstamp: Option, + pub ext_respid: Option, +} + +pub mod ext { + use crate::{ + common::{ZExtZ64, ZExtZBuf}, + zextz64, zextzbuf, + }; + pub type QoS = zextz64!(0x1, false); + pub type QoSType = crate::network::ext::QoSType<{ QoS::ID }>; + + pub type Timestamp = zextzbuf!(0x2, false); + pub type TimestampType = crate::network::ext::TimestampType<{ Timestamp::ID }>; + + pub type ResponderId = zextzbuf!(0x3, false); + pub type ResponderIdType = crate::network::ext::EntityIdType<{ ResponderId::ID }>; +} + +impl Response { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); + + let rid: RequestId = rng.gen(); + let wire_expr = WireExpr::rand(); + let payload = ResponseBody::rand(); + let ext_qos = ext::QoSType::rand(); + let ext_tstamp = rng.gen_bool(0.5).then(ext::TimestampType::rand); + let ext_respid = rng.gen_bool(0.5).then(ext::ResponderIdType::rand); + + Self { + rid, + wire_expr, + payload, + ext_qos, + ext_tstamp, + ext_respid, + } + } +} + +/// # ResponseFinal message +/// +/// ```text +/// Flags: +/// - X: Reserved +/// - X: Reserved +/// - Z: Extension If Z==1 then at least one extension is present +/// +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// |Z|X|X| ResFinal| +/// +-+-+-+---------+ +/// ~ request_id:z32~ (*) +/// +---------------+ +/// ~ [reply_exts] ~ if Z==1 +/// +---------------+ +/// +/// (*) The resolution of the request id is negotiated during the session establishment. +/// This implementation limits the resolution to 32bit. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ResponseFinal { + pub rid: RequestId, + pub ext_qos: ext::QoSType, + pub ext_tstamp: Option, +} + +impl ResponseFinal { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + + let mut rng = rand::thread_rng(); + let rid: RequestId = rng.gen(); + let ext_qos = ext::QoSType::rand(); + let ext_tstamp = rng.gen_bool(0.5).then(ext::TimestampType::rand); + + Self { + rid, + ext_qos, + ext_tstamp, + } + } +} diff --git a/commons/zenoh-protocol/src/scouting/hello.rs b/commons/zenoh-protocol/src/scouting/hello.rs index fc110e62b6..562e2fb8c4 100644 --- a/commons/zenoh-protocol/src/scouting/hello.rs +++ b/commons/zenoh-protocol/src/scouting/hello.rs @@ -17,40 +17,92 @@ use core::fmt; /// # Hello message /// +/// The [`Hello`] message is used to advertise the locators a zenoh node is reachable at. +/// The [`Hello`] message SHOULD be sent in a unicast fashion in response to a [`super::Scout`] +/// message as shown below: +/// /// ```text -/// NOTE: 16 bits (2 bytes) may be prepended to the serialized message indicating the total length -/// in bytes of the message, resulting in the maximum length of a message being 65_535 bytes. -/// This is necessary in those stream-oriented transports (e.g., TCP) that do not preserve -/// the boundary of the serialized messages. The length is encoded as little-endian. -/// In any case, the length of a message must not exceed 65_535 bytes. +/// A B C +/// | SCOUT | | +/// |------------------>| | +/// | \---------------------------->| +/// | | | +/// | HELLO | | +/// |<------------------| | +/// | | HELLO | +/// |<--------------------------------------| +/// | | | +/// ``` +/// +/// Moreover, a [`Hello`] message MAY be sent in the network in a multicast +/// fashion to advertise the presence of zenoh node. The advertisement operation MAY be performed +/// periodically as shown below: +/// +///```text +/// A B C +/// | HELLO | | +/// |------------------>| | +/// | \---------------------------->| +/// | | | +/// ~ ... ~ ... ~ +/// | | | +/// | HELLO | | +/// |------------------>| | +/// | \---------------------------->| +/// | | | +/// ~ ... ~ ... ~ +/// | | | +/// ``` +/// +/// Examples of locators included in the [`Hello`] message are: +/// +/// ```text +/// udp/192.168.1.1:7447 +/// tcp/192.168.1.1:7447 +/// udp/224.0.0.224:7447 +/// tcp/localhost:7447 +/// ``` /// -/// The HELLO message is sent in any of the following three cases: -/// 1) in response to a SCOUT message; -/// 2) to (periodically) advertise (e.g., on multicast) the Peer and the locators it is reachable at; -/// 3) in a already established transport to update the corresponding peer on the new capabilities -/// (i.e., whatmai) and/or new set of locators (i.e., added or deleted). -/// Locators are expressed as: -/// -/// udp/192.168.0.2:1234 -/// tcp/192.168.0.2:1234 -/// udp/239.255.255.123:5555 -/// +/// The [`Hello`] message structure is defined as follows: +/// +/// ```text +/// Header flags: +/// - L: Locators If L==1 then the list of locators is present, else the src address is the locator +/// - X: Reserved +/// - Z: Extensions If Z==1 then zenoh extensions will follow. /// /// 7 6 5 4 3 2 1 0 /// +-+-+-+-+-+-+-+-+ -/// |L|W|I| HELLO | -/// +-+-+-+-+-------+ -/// ~ peer-id ~ if I==1 +/// |Z|X|L| HELLO | +/// +-+-+-+---------+ +/// | version | /// +---------------+ -/// ~ whatami ~ if W==1 -- Otherwise it is from a Router +/// |zid_len|X|X|wai| (*) +/// +-+-+-+-+-+-+-+-+ +/// ~ [u8] ~ -- ZenohID /// +---------------+ -/// ~ [Locators] ~ if L==1 -- Otherwise src-address is the locator +/// ~ ~ if Flag(L)==1 -- List of locators /// +---------------+ +/// +/// (*) WhatAmI. It indicates the role of the zenoh node sending the HELLO message. +/// The valid WhatAmI values are: +/// - 0b00: Router +/// - 0b01: Peer +/// - 0b10: Client +/// - 0b11: Reserved /// ``` +/// +pub mod flag { + pub const L: u8 = 1 << 5; // 0x20 Locators if L==1 then the list of locators is present, else the src address is the locator + // pub const X: u8 = 1 << 6; // 0x40 Reserved + pub const Z: u8 = 1 << 7; // 0x80 Extensions if Z==1 then an extension will follow +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct Hello { - pub zid: Option, + pub version: u8, pub whatami: WhatAmI, + pub zid: ZenohId, pub locators: Vec, } @@ -71,11 +123,8 @@ impl Hello { let mut rng = rand::thread_rng(); - let zid = if rng.gen_bool(0.5) { - Some(ZenohId::default()) - } else { - None - }; + let version: u8 = rng.gen(); + let zid = ZenohId::default(); let whatami = WhatAmI::rand(); let locators = if rng.gen_bool(0.5) { Vec::from_iter((1..5).map(|_| Locator::rand())) @@ -83,6 +132,7 @@ impl Hello { vec![] }; Self { + version, zid, whatami, locators, diff --git a/commons/zenoh-protocol/src/scouting/mod.rs b/commons/zenoh-protocol/src/scouting/mod.rs index 4044a93b7a..9e7fd27c2d 100644 --- a/commons/zenoh-protocol/src/scouting/mod.rs +++ b/commons/zenoh-protocol/src/scouting/mod.rs @@ -11,16 +11,17 @@ // Contributors: // ZettaScale Zenoh Team, // -mod hello; -mod scout; +pub mod hello; +pub mod scout; -use crate::{ - common::Attachment, - core::{whatami::WhatAmIMatcher, Locator, WhatAmI, ZenohId}, -}; -use alloc::vec::Vec; -pub use hello::*; -pub use scout::*; +pub use hello::Hello; +pub use scout::Scout; + +pub mod id { + // Scouting Messages + pub const SCOUT: u8 = 0x01; + pub const HELLO: u8 = 0x02; +} // Zenoh messages at scouting level #[derive(Debug, Clone, PartialEq, Eq)] @@ -32,64 +33,44 @@ pub enum ScoutingBody { #[derive(Debug, Clone, PartialEq, Eq)] pub struct ScoutingMessage { pub body: ScoutingBody, - pub attachment: Option, #[cfg(feature = "stats")] pub size: Option, } impl ScoutingMessage { - pub fn make_scout( - what: Option, - zid_request: bool, - attachment: Option, - ) -> ScoutingMessage { - ScoutingMessage { - body: ScoutingBody::Scout(Scout { what, zid_request }), - attachment, - #[cfg(feature = "stats")] - size: None, + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + + let mut rng = rand::thread_rng(); + + match rng.gen_range(0..2) { + 0 => ScoutingBody::Scout(Scout::rand()), + 1 => ScoutingBody::Hello(Hello::rand()), + _ => unreachable!(), } + .into() } +} - pub fn make_hello( - zid: Option, - whatami: Option, - locators: Option>, - attachment: Option, - ) -> ScoutingMessage { - let whatami = whatami.unwrap_or(WhatAmI::Router); - let locators = locators.unwrap_or_default(); - - ScoutingMessage { - body: ScoutingBody::Hello(Hello { - zid, - whatami, - locators, - }), - attachment, +impl From for ScoutingMessage { + fn from(body: ScoutingBody) -> Self { + Self { + body, #[cfg(feature = "stats")] size: None, } } +} - #[cfg(feature = "test")] - pub fn rand() -> Self { - use rand::Rng; - - let mut rng = rand::thread_rng(); - - let attachment = if rng.gen_bool(0.5) { - Some(Attachment::rand()) - } else { - None - }; - - let body = match rng.gen_range(0..2) { - 0 => ScoutingBody::Hello(Hello::rand()), - 1 => ScoutingBody::Scout(Scout::rand()), - _ => unreachable!(), - }; +impl From for ScoutingMessage { + fn from(scout: Scout) -> Self { + ScoutingBody::Scout(scout).into() + } +} - Self { body, attachment } +impl From for ScoutingMessage { + fn from(hello: Hello) -> Self { + ScoutingBody::Hello(hello).into() } } diff --git a/commons/zenoh-protocol/src/scouting/scout.rs b/commons/zenoh-protocol/src/scouting/scout.rs index 16ec9cf42c..8cdb47d3cf 100644 --- a/commons/zenoh-protocol/src/scouting/scout.rs +++ b/commons/zenoh-protocol/src/scouting/scout.rs @@ -11,30 +11,71 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::core::whatami::WhatAmIMatcher; +use crate::core::{whatami::WhatAmIMatcher, ZenohId}; /// # Scout message /// +/// The [`Scout`] message MAY be sent at any point in time to discover the available zenoh nodes in the +/// network. The [`Scout`] message SHOULD be sent in a multicast or broadcast fashion. Upon receiving a +/// [`Scout`] message, a zenoh node MUST first verify whether the matching criteria are satisfied, then +/// it SHOULD reply with a [`super::Hello`] message in a unicast fashion including all the requested +/// information. +/// +/// The scouting message flow is the following: +/// /// ```text -/// NOTE: 16 bits (2 bytes) may be prepended to the serialized message indicating the total length -/// in bytes of the message, resulting in the maximum length of a message being 65_535 bytes. -/// This is necessary in those stream-oriented transports (e.g., TCP) that do not preserve -/// the boundary of the serialized messages. The length is encoded as little-endian. -/// In any case, the length of a message must not exceed 65_535 bytes. +/// A B C +/// | SCOUT | | +/// |------------------>| | +/// | \---------------------------->| +/// | | | +/// | HELLO | | +/// |<------------------| | +/// | | HELLO | +/// |<--------------------------------------| +/// | | | +/// ``` /// -/// The SCOUT message can be sent at any point in time to solicit HELLO messages from matching parties. +/// The SCOUT message structure is defined as follows: +/// +/// ```text +/// Header flags: +/// - X: Reserved +/// - X: Reserved +/// - Z: Extensions If Z==1 then zenoh extensions will follow. /// /// 7 6 5 4 3 2 1 0 /// +-+-+-+-+-+-+-+-+ -/// |X|W|I| SCOUT | -/// +-+-+-+-+-------+ -/// ~ what ~ if W==1 -- Otherwise implicitly scouting for Routers +/// |Z|X|X| SCOUT | +/// +-+-+-+---------+ +/// | version | /// +---------------+ +/// |zid_len|I| what| (#)(*) +/// +-+-+-+-+-+-+-+-+ +/// ~ [u8] ~ if Flag(I)==1 -- ZenohID +/// +---------------+ +/// +/// (#) ZID length. If Flag(I)==1 it indicates how many bytes are used for the ZenohID bytes. +/// A ZenohID is minimum 1 byte and maximum 16 bytes. Therefore, the actual lenght is computed as: +/// real_zid_len := 1 + zid_len +/// +/// (*) What. It indicates a bitmap of WhatAmI interests. +/// The valid bitflags are: +/// - 0b001: Router +/// - 0b010: Peer +/// - 0b100: Client /// ``` +pub mod flag { + pub const I: u8 = 1 << 3; // 0x04 ZenohID if I==1 then the ZenohID is present + // pub const X: u8 = 1 << 6; // 0x40 Reserved + pub const Z: u8 = 1 << 7; // 0x80 Extensions if Z==1 then an extension will follow +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct Scout { - pub what: Option, - pub zid_request: bool, + pub version: u8, + pub what: WhatAmIMatcher, + pub zid: Option, } impl Scout { @@ -44,12 +85,9 @@ impl Scout { let mut rng = rand::thread_rng(); - let what = if rng.gen_bool(0.5) { - Some(WhatAmIMatcher::rand()) - } else { - None - }; - let zid_request = rng.gen_bool(0.5); - Self { what, zid_request } + let version: u8 = rng.gen(); + let what = WhatAmIMatcher::rand(); + let zid = rng.gen_bool(0.5).then_some(ZenohId::rand()); + Self { version, what, zid } } } diff --git a/commons/zenoh-protocol/src/transport/close.rs b/commons/zenoh-protocol/src/transport/close.rs index f9542f457e..4e760400b7 100644 --- a/commons/zenoh-protocol/src/transport/close.rs +++ b/commons/zenoh-protocol/src/transport/close.rs @@ -11,40 +11,78 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::core::ZenohId; /// # Close message /// +/// The [`Close`] message is sent in any of the following two cases: +/// 1) in response to an INIT or OPEN message which are not accepted; +/// 2) at any time to arbitrarly close the transport with the corresponding zenoh node. +/// +/// The [`Close`] message flow is the following: +/// /// ```text -/// NOTE: 16 bits (2 bytes) may be prepended to the serialized message indicating the total length -/// in bytes of the message, resulting in the maximum length of a message being 65_535 bytes. -/// This is necessary in those stream-oriented transports (e.g., TCP) that do not preserve -/// the boundary of the serialized messages. The length is encoded as little-endian. -/// In any case, the length of a message must not exceed 65_535 bytes. +/// A B +/// | CLOSE | +/// |------------------>| +/// | | +/// ``` /// -/// The CLOSE message is sent in any of the following two cases: -/// 1) in response to an OPEN message which is not accepted; -/// 2) at any time to arbitrarly close the transport with the corresponding peer. +/// The [`Close`] message structure is defined as follows: +/// +/// ```text +/// Flags: +/// - S: Session close If S==1 close the whole session, close only the link otherwise +/// - X: Reserved +/// - Z: Extensions If Z==1 then zenoh extensions will follow. /// /// 7 6 5 4 3 2 1 0 /// +-+-+-+-+-+-+-+-+ -/// |X|K|I| CLOSE | -/// +-+-+-+-+-------+ -/// ~ peer_id ~ if I==1 -- PID of the target peer. -/// +---------------+ +/// |Z|X|S| CLOSE | +/// +-+-+-+---------+ /// | reason | /// +---------------+ -/// -/// - if K==0 then close the whole zenoh transport. -/// - if K==1 then close the transport link the CLOSE message was sent on (e.g., TCP socket) but -/// keep the whole transport open. NOTE: the transport will be automatically closed when -/// the transport's lease period expires. +/// ~ [CloseExts] ~ if Flag(Z)==1 +/// +---------------+ /// ``` +/// NOTE: 16 bits (2 bytes) may be prepended to the serialized message indicating the total length +/// in bytes of the message, resulting in the maximum length of a message being 65535 bytes. +/// This is necessary in those stream-oriented transports (e.g., TCP) that do not preserve +/// the boundary of the serialized messages. The length is encoded as little-endian. +/// In any case, the length of a message must not exceed 65535 bytes. +/// + +pub mod flag { + pub const S: u8 = 1 << 5; // 0x20 Session close if S==1 close the whole session, close only the link otherwise + // pub const X: u8 = 1 << 6; // 0x40 Reserved + pub const Z: u8 = 1 << 7; // 0x80 Extensions if Z==1 then an extension will follow +} + +// Reason for the Close message +pub mod reason { + pub const GENERIC: u8 = 0x00; + pub const UNSUPPORTED: u8 = 0x01; + pub const INVALID: u8 = 0x02; + pub const MAX_SESSIONS: u8 = 0x03; + pub const MAX_LINKS: u8 = 0x04; + pub const EXPIRED: u8 = 0x05; +} + +pub fn reason_to_str(reason: u8) -> &'static str { + match reason { + reason::GENERIC => "GENERIC", + reason::UNSUPPORTED => "UNSUPPORTED", + reason::INVALID => "INVALID", + reason::MAX_SESSIONS => "MAX_SESSIONS", + reason::MAX_LINKS => "MAX_LINKS", + reason::EXPIRED => "EXPIRED", + _ => "UNKNOWN", + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct Close { - pub zid: Option, pub reason: u8, - pub link_only: bool, + pub session: bool, } impl Close { @@ -54,18 +92,9 @@ impl Close { let mut rng = rand::thread_rng(); - let zid = if rng.gen_bool(0.5) { - Some(ZenohId::default()) - } else { - None - }; let reason: u8 = rng.gen(); - let link_only = rng.gen_bool(0.5); + let session = rng.gen_bool(0.5); - Self { - zid, - reason, - link_only, - } + Self { reason, session } } } diff --git a/commons/zenoh-protocol/src/transport/fragment.rs b/commons/zenoh-protocol/src/transport/fragment.rs new file mode 100644 index 0000000000..3e80c9cfbf --- /dev/null +++ b/commons/zenoh-protocol/src/transport/fragment.rs @@ -0,0 +1,138 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::core::Reliability; +pub use crate::transport::TransportSn; +use zenoh_buffers::ZSlice; + +/// # Fragment message +/// +/// The [`Fragment`] message is used to transmit on the wire large [`crate::zenoh::ZenohMessage`] +/// that require fragmentation because they are larger thatn the maximum batch size +/// (i.e. 2^16-1) and/or the link MTU. +/// +/// The [`Fragment`] message flow is the following: +/// +/// ```text +/// A B +/// | FRAGMENT(MORE) | +/// |------------------>| +/// | FRAGMENT(MORE) | +/// |------------------>| +/// | FRAGMENT(MORE) | +/// |------------------>| +/// | FRAGMENT | +/// |------------------>| +/// | | +/// ``` +/// +/// The [`Fragment`] message structure is defined as follows: +/// +/// ```text +/// Flags: +/// - R: Reliable If R==1 it concerns the reliable channel, else the best-effort channel +/// - M: More If M==1 then other fragments will follow +/// - Z: Extensions If Z==1 then zenoh extensions will follow. +/// +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// |Z|M|R| FRAGMENT| +/// +-+-+-+---------+ +/// % seq num % +/// +---------------+ +/// ~ [FragExts] ~ if Flag(Z)==1 +/// +---------------+ +/// ~ [u8] ~ +/// +---------------+ +/// ``` +/// NOTE: 16 bits (2 bytes) may be prepended to the serialized message indicating the total length +/// in bytes of the message, resulting in the maximum length of a message being 65535 bytes. +/// This is necessary in those stream-oriented transports (e.g., TCP) that do not preserve +/// the boundary of the serialized messages. The length is encoded as little-endian. +/// In any case, the length of a message must not exceed 65535 bytes. +/// +pub mod flag { + pub const R: u8 = 1 << 5; // 0x20 Reliable if R==1 then the frame is reliable + pub const M: u8 = 1 << 6; // 0x40 More if M==1 then another fragment will follow + pub const Z: u8 = 1 << 7; // 0x80 Extensions if Z==1 then an extension will follow +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Fragment { + pub reliability: Reliability, + pub more: bool, + pub sn: TransportSn, + pub payload: ZSlice, + pub ext_qos: ext::QoSType, +} + +// Extensions +pub mod ext { + use crate::{common::ZExtZ64, zextz64}; + + pub type QoS = zextz64!(0x1, true); + pub type QoSType = crate::transport::ext::QoSType<{ QoS::ID }>; +} + +impl Fragment { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + + let mut rng = rand::thread_rng(); + + let reliability = Reliability::rand(); + let more = rng.gen_bool(0.5); + let sn: TransportSn = rng.gen(); + let payload = ZSlice::rand(rng.gen_range(8..128)); + let ext_qos = ext::QoSType::rand(); + + Fragment { + reliability, + sn, + more, + payload, + ext_qos, + } + } +} + +// FragmentHeader +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct FragmentHeader { + pub reliability: Reliability, + pub more: bool, + pub sn: TransportSn, + pub ext_qos: ext::QoSType, +} + +impl FragmentHeader { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + + let mut rng = rand::thread_rng(); + + let reliability = Reliability::rand(); + let more = rng.gen_bool(0.5); + let sn: TransportSn = rng.gen(); + let ext_qos = ext::QoSType::rand(); + + FragmentHeader { + reliability, + more, + sn, + ext_qos, + } + } +} diff --git a/commons/zenoh-protocol/src/transport/frame.rs b/commons/zenoh-protocol/src/transport/frame.rs index e95dd15ab2..bcd01e7965 100644 --- a/commons/zenoh-protocol/src/transport/frame.rs +++ b/commons/zenoh-protocol/src/transport/frame.rs @@ -11,177 +11,125 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::{ - core::{Channel, ZInt}, - zenoh::ZenohMessage, -}; +use crate::{core::Reliability, network::NetworkMessage, transport::TransportSn}; use alloc::vec::Vec; -use zenoh_buffers::ZSlice; /// # Frame message /// +/// The [`Frame`] message is used to transmit one ore more complete serialized +/// [`crate::net::protocol::message::ZenohMessage`]. I.e., the total length of the +/// serialized [`crate::net::protocol::message::ZenohMessage`] (s) MUST be smaller +/// than the maximum batch size (i.e. 2^16-1) and the link MTU. +/// The [`Frame`] message is used as means to aggreate multiple +/// [`crate::net::protocol::message::ZenohMessage`] in a single atomic message that +/// goes on the wire. By doing so, many small messages can be batched together and +/// share common information like the sequence number. +/// +/// The [`Frame`] message flow is the following: +/// /// ```text -/// NOTE: 16 bits (2 bytes) may be prepended to the serialized message indicating the total length -/// in bytes of the message, resulting in the maximum length of a message being 65_535 bytes. -/// This is necessary in those stream-oriented transports (e.g., TCP) that do not preserve -/// the boundary of the serialized messages. The length is encoded as little-endian. -/// In any case, the length of a message must not exceed 65_535 bytes. +/// A B +/// | FRAME | +/// |------------------>| +/// | | +/// ``` +/// +/// The [`Frame`] message structure is defined as follows: +/// +/// ```text +/// Flags: +/// - R: Reliable If R==1 it concerns the reliable channel, else the best-effort channel +/// - X: Reserved +/// - Z: Extensions If Z==1 then zenoh extensions will follow. /// /// 7 6 5 4 3 2 1 0 /// +-+-+-+-+-+-+-+-+ -/// |E|F|R| FRAME | -/// +-+-+-+-+-------+ -/// ~ SN ~ +/// |Z|X|R| FRAME | +/// +-+-+-+---------+ +/// % seq num % +/// +---------------+ +/// ~ [FrameExts] ~ if Flag(Z)==1 /// +---------------+ -/// ~ FramePayload ~ -- if F==1 then the payload is a fragment of a single Zenoh Message, a list of complete Zenoh Messages otherwise. +/// ~ [NetworkMsg] ~ /// +---------------+ +/// ``` /// -/// - if R==1 then the FRAME is sent on the reliable channel, best-effort otherwise. -/// - if F==1 then the FRAME is a fragment. -/// - if E==1 then the FRAME is the last fragment. E==1 is valid iff F==1. +/// NOTE: 16 bits (2 bytes) may be prepended to the serialized message indicating the total length +/// in bytes of the message, resulting in the maximum length of a message being 65535 bytes. +/// This is necessary in those stream-oriented transports (e.g., TCP) that do not preserve +/// the boundary of the serialized messages. The length is encoded as little-endian. +/// In any case, the length of a message must not exceed 65535 bytes. /// -/// NOTE: Only one bit would be sufficient to signal fragmentation in a IP-like fashion as follows: -/// - if F==1 then this FRAME is a fragment and more fragment will follow; -/// - if F==0 then the message is the last fragment if SN-1 had F==1, -/// otherwise it's a non-fragmented message. -/// However, this would require to always perform a two-steps de-serialization: first -/// de-serialize the FRAME and then the Payload. This is due to the fact the F==0 is ambigous -/// w.r.t. detecting if the FRAME is a fragment or not before SN re-ordering has occured. -/// By using the F bit to only signal whether the FRAME is fragmented or not, it allows to -/// de-serialize the payload in one single pass when F==0 since no re-ordering needs to take -/// place at this stage. Then, the F bit is used to detect the last fragment during re-ordering. -/// ``` +pub mod flag { + pub const R: u8 = 1 << 5; // 0x20 Reliable if R==1 then the frame is reliable + // pub const X: u8 = 1 << 6; // 0x40 Reserved + pub const Z: u8 = 1 << 7; // 0x80 Extensions if Z==1 then an extension will follow +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct Frame { - pub channel: Channel, - pub sn: ZInt, - pub payload: FramePayload, + pub reliability: Reliability, + pub sn: TransportSn, + pub payload: Vec, + pub ext_qos: ext::QoSType, } -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum FramePayload { - /// ```text - /// The Payload of a fragmented Frame. - /// - /// 7 6 5 4 3 2 1 0 - /// +-+-+-+-+-+-+-+-+ - /// ~ payload bytes ~ - /// +---------------+ - /// ``` - Fragment { buffer: ZSlice, is_final: bool }, - /// ```text - /// The Payload of a batched Frame. - /// - /// 7 6 5 4 3 2 1 0 - /// +-+-+-+-+-+-+-+-+ - /// ~ ZenohMessage ~ - /// +---------------+ - /// ~ ... ~ - Additional complete Zenoh messages. - /// +---------------+ - /// - /// NOTE: A batched Frame must contain at least one complete Zenoh message. - /// There is no upper limit to the number of Zenoh messages that can - /// be batched together in the same frame. - /// ``` - Messages { messages: Vec }, +// Extensions +pub mod ext { + use crate::{common::ZExtZ64, zextz64}; + + pub type QoS = zextz64!(0x1, true); + pub type QoSType = crate::transport::ext::QoSType<{ QoS::ID }>; } impl Frame { #[cfg(feature = "test")] pub fn rand() -> Self { - use crate::core::{Priority, Reliability}; use rand::Rng; - const MIN: usize = 1; - const MAX: usize = 1_024; - let mut rng = rand::thread_rng(); - let priority: Priority = rng - .gen_range(Priority::MAX as u8..=Priority::MIN as u8) - .try_into() - .unwrap(); - let reliability = if rng.gen_bool(0.5) { - Reliability::Reliable - } else { - Reliability::BestEffort - }; - let channel = Channel { - priority, - reliability, - }; - let sn: ZInt = rng.gen(); - let payload = if rng.gen_bool(0.5) { - FramePayload::Fragment { - buffer: ZSlice::rand(rng.gen_range(MIN..=MAX)), - is_final: rng.gen_bool(0.5), - } - } else { - let n = rng.gen_range(1..16); - let messages = (0..n) - .map(|_| { - let mut m = ZenohMessage::rand(); - m.channel = channel; - m - }) - .collect::>(); - FramePayload::Messages { messages } - }; + let reliability = Reliability::rand(); + let sn: TransportSn = rng.gen(); + let ext_qos = ext::QoSType::rand(); + let mut payload = vec![]; + for _ in 0..rng.gen_range(1..4) { + let m = NetworkMessage::rand(); + payload.push(m); + } Frame { - channel, + reliability, sn, + ext_qos, payload, } } } // FrameHeader -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(u8)] -pub enum FrameKind { - Messages, - SomeFragment, - LastFragment, -} - -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct FrameHeader { - pub channel: Channel, - pub sn: ZInt, - pub kind: FrameKind, + pub reliability: Reliability, + pub sn: TransportSn, + pub ext_qos: ext::QoSType, } impl FrameHeader { #[cfg(feature = "test")] pub fn rand() -> Self { - use crate::core::{Priority, Reliability}; - use rand::{seq::SliceRandom, Rng}; + use rand::Rng; let mut rng = rand::thread_rng(); - let priority: Priority = rng - .gen_range(Priority::MAX as u8..=Priority::MIN as u8) - .try_into() - .unwrap(); - let reliability = if rng.gen_bool(0.5) { - Reliability::Reliable - } else { - Reliability::BestEffort - }; - let channel = Channel { - priority, - reliability, - }; - let sn: ZInt = rng.gen(); - let kind = *[ - FrameKind::Messages, - FrameKind::SomeFragment, - FrameKind::LastFragment, - ] - .choose(&mut rng) - .unwrap(); + let reliability = Reliability::rand(); + let sn: TransportSn = rng.gen(); + let ext_qos = ext::QoSType::rand(); - FrameHeader { channel, sn, kind } + FrameHeader { + reliability, + sn, + ext_qos, + } } } diff --git a/commons/zenoh-protocol/src/transport/init.rs b/commons/zenoh-protocol/src/transport/init.rs index fc495b8ba5..566b6c91b1 100644 --- a/commons/zenoh-protocol/src/transport/init.rs +++ b/commons/zenoh-protocol/src/transport/init.rs @@ -11,58 +11,142 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::core::{WhatAmI, ZInt, ZenohId}; +use crate::{ + core::{Resolution, WhatAmI, ZenohId}, + transport::BatchSize, +}; use zenoh_buffers::ZSlice; /// # Init message /// +/// The INIT message is sent on a specific Locator to initiate a transport with the zenoh node +/// associated with that Locator. The initiator MUST send an INIT message with the A flag set to 0. +/// If the corresponding zenohd node deems appropriate to accept the INIT message, the corresponding +/// peer MUST reply with an INIT message with the A flag set to 1. Alternatively, it MAY reply with +/// a [`super::Close`] message. For convenience, we call [`InitSyn`] and [`InitAck`] an INIT message +/// when the A flag is set to 0 and 1, respectively. +/// +/// The [`InitSyn`]/[`InitAck`] message flow is the following: +/// /// ```text -/// NOTE: 16 bits (2 bytes) may be prepended to the serialized message indicating the total length -/// in bytes of the message, resulting in the maximum length of a message being 65_535 bytes. -/// This is necessary in those stream-oriented transports (e.g., TCP) that do not preserve -/// the boundary of the serialized messages. The length is encoded as little-endian. -/// In any case, the length of a message must not exceed 65_535 bytes. +/// A B +/// | INIT SYN | +/// |------------------>| +/// | | +/// | INIT ACK | +/// |<------------------| +/// | | +/// ``` /// -/// The INIT message is sent on a specific Locator to initiate a transport with the peer associated -/// with that Locator. The initiator MUST send an INIT message with the A flag set to 0. If the -/// corresponding peer deems appropriate to initialize a transport with the initiator, the corresponding -/// peer MUST reply with an INIT message with the A flag set to 1. +/// The INIT message structure is defined as follows: +/// +/// ```text +/// Flags: +/// - A: Ack If A==0 then the message is an InitSyn else it is an InitAck +/// - S: Size params If S==1 then size parameters are exchanged +/// - Z: Extensions If Z==1 then zenoh extensions will follow. /// /// 7 6 5 4 3 2 1 0 /// +-+-+-+-+-+-+-+-+ -/// |O|S|A| INIT | -/// +-+-+-+-+-------+ -/// ~ |Q~ if O==1 +/// |Z|S|A| INIT | +/// +-+-+-+---------+ +/// | version | /// +---------------+ -/// | v_maj | v_min | if A==0 -- Protocol Version VMaj.VMin -/// +-------+-------+ -/// ~ whatami ~ -- Client, Router, Peer or a combination of them +/// |zid_len|x|x|wai| (#)(*) +/// +-------+-+-+---+ +/// ~ [u8] ~ -- ZenohID of the sender of the INIT message /// +---------------+ -/// ~ peer_id ~ -- PID of the sender of the INIT message +/// |x|x|x|x|rid|fsn| \ -- SN/ID resolution (+) +/// +---------------+ | if Flag(S)==1 +/// | u16 | | -- Batch Size ($) +/// | | / /// +---------------+ -/// ~ sn_resolution ~ if S==1 -- the sequence number resolution(*) +/// ~ ~ -- if Flag(A)==1 -- Cookie /// +---------------+ -/// ~ cookie ~ if A==1 +/// ~ [InitExts] ~ -- if Flag(Z)==1 /// +---------------+ /// -/// (*) if A==0 and S==0 then 2^28 is assumed. -/// if A==1 and S==0 then the agreed resolution is the one communicated by the initiator. +/// If A==1 and S==0 then size parameters are (ie. S flag) are accepted. /// -/// - if Q==1 then the initiator/responder support QoS. +/// (*) WhatAmI. It indicates the role of the zenoh node sending the INIT message. +/// The valid WhatAmI values are: +/// - 0b00: Router +/// - 0b01: Peer +/// - 0b10: Client +/// - 0b11: Reserved +/// +/// (#) ZID length. It indicates how many bytes are used for the ZenohID bytes. +/// A ZenohID is minimum 1 byte and maximum 16 bytes. Therefore, the actual lenght is computed as: +/// real_zid_len := 1 + zid_len +/// +/// (+) Sequence Number/ID resolution. It indicates the resolution and consequently the wire overhead +/// of various SN and ID in Zenoh. +/// - fsn: frame/fragment sequence number resolution. Used in Frame/Fragment messages. +/// - rid: request ID resolution. Used in Request/Response messages. +/// The valid SN/ID resolution values are: +/// - 0b00: 8 bits +/// - 0b01: 16 bits +/// - 0b10: 32 bits +/// - 0b11: 64 bits +/// +/// ($) Batch Size. It indicates the maximum size of a batch the sender of the INIT message is willing +/// to accept when reading from the network. Default on unicast: 65535. +/// +/// NOTE: 16 bits (2 bytes) may be prepended to the serialized message indicating the total length +/// in bytes of the message, resulting in the maximum length of a message being 65535 bytes. +/// This is necessary in those stream-oriented transports (e.g., TCP) that do not preserve +/// the boundary of the serialized messages. The length is encoded as little-endian. +/// In any case, the length of a message must not exceed 65535 bytes. /// ``` +/// + +pub mod flag { + pub const A: u8 = 1 << 5; // 0x20 Ack if A==0 then the message is an InitSyn else it is an InitAck + pub const S: u8 = 1 << 6; // 0x40 Size params if S==1 then size parameters are exchanged + pub const Z: u8 = 1 << 7; // 0x80 Extensions if Z==1 then an extension will follow +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct InitSyn { pub version: u8, pub whatami: WhatAmI, pub zid: ZenohId, - pub sn_resolution: ZInt, - pub is_qos: bool, + pub resolution: Resolution, + pub batch_size: BatchSize, + pub ext_qos: Option, + pub ext_shm: Option, + pub ext_auth: Option, + pub ext_mlink: Option, +} + +// Extensions +pub mod ext { + use crate::{ + common::{ZExtUnit, ZExtZBuf}, + zextunit, zextzbuf, + }; + + /// # QoS extension + /// Used to negotiate the use of QoS + pub type QoS = zextunit!(0x1, false); + + /// # Shm extension + /// Used as challenge for probing shared memory capabilities + pub type Shm = zextzbuf!(0x2, false); + + /// # Auth extension + /// Used as challenge for probing authentication rights + pub type Auth = zextzbuf!(0x3, false); + + /// # Multilink extension + /// Used as challenge for probing multilink capabilities + pub type MultiLink = zextzbuf!(0x4, false); } impl InitSyn { #[cfg(feature = "test")] pub fn rand() -> Self { - use crate::defaults::SEQ_NUM_RES; + use crate::common::{ZExtUnit, ZExtZBuf}; use rand::Rng; let mut rng = rand::thread_rng(); @@ -70,58 +154,75 @@ impl InitSyn { let version: u8 = rng.gen(); let whatami = WhatAmI::rand(); let zid = ZenohId::default(); - let sn_resolution = if rng.gen_bool(0.5) { - rng.gen() - } else { - SEQ_NUM_RES - }; - let is_qos = rng.gen_bool(0.5); + let resolution = Resolution::rand(); + let batch_size: u16 = rng.gen(); + let ext_qos = rng.gen_bool(0.5).then_some(ZExtUnit::rand()); + let ext_shm = rng.gen_bool(0.5).then_some(ZExtZBuf::rand()); + let ext_auth = rng.gen_bool(0.5).then_some(ZExtZBuf::rand()); + let ext_mlink = rng.gen_bool(0.5).then_some(ZExtZBuf::rand()); Self { version, whatami, zid, - sn_resolution, - is_qos, + resolution, + batch_size, + ext_qos, + ext_shm, + ext_auth, + ext_mlink, } } } #[derive(Debug, Clone, PartialEq, Eq)] pub struct InitAck { + pub version: u8, pub whatami: WhatAmI, pub zid: ZenohId, - pub sn_resolution: Option, - pub is_qos: bool, + pub resolution: Resolution, + pub batch_size: BatchSize, pub cookie: ZSlice, + pub ext_qos: Option, + pub ext_shm: Option, + pub ext_auth: Option, + pub ext_mlink: Option, } impl InitAck { #[cfg(feature = "test")] pub fn rand() -> Self { + use crate::common::{ZExtUnit, ZExtZBuf}; use rand::Rng; - const MIN: usize = 32; - const MAX: usize = 1_024; - let mut rng = rand::thread_rng(); + let version: u8 = rng.gen(); let whatami = WhatAmI::rand(); let zid = ZenohId::default(); - let sn_resolution = if rng.gen_bool(0.5) { - Some(rng.gen()) + let resolution = if rng.gen_bool(0.5) { + Resolution::default() } else { - None + Resolution::rand() }; - let is_qos = rng.gen_bool(0.5); - let cookie = ZSlice::rand(rng.gen_range(MIN..=MAX)); + let batch_size: u16 = rng.gen(); + let cookie = ZSlice::rand(64); + let ext_qos = rng.gen_bool(0.5).then_some(ZExtUnit::rand()); + let ext_shm = rng.gen_bool(0.5).then_some(ZExtZBuf::rand()); + let ext_auth = rng.gen_bool(0.5).then_some(ZExtZBuf::rand()); + let ext_mlink = rng.gen_bool(0.5).then_some(ZExtZBuf::rand()); Self { + version, whatami, zid, - sn_resolution, - is_qos, + resolution, + batch_size, cookie, + ext_qos, + ext_shm, + ext_auth, + ext_mlink, } } } diff --git a/commons/zenoh-protocol/src/transport/join.rs b/commons/zenoh-protocol/src/transport/join.rs index 17d0484651..00920c17ee 100644 --- a/commons/zenoh-protocol/src/transport/join.rs +++ b/commons/zenoh-protocol/src/transport/join.rs @@ -11,70 +11,128 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::core::{ConduitSnList, WhatAmI, ZInt, ZenohId}; +use crate::{ + core::{Priority, Resolution, WhatAmI, ZenohId}, + transport::{BatchSize, PrioritySn}, +}; use core::time::Duration; /// # Join message /// +/// The JOIN message is sent on a multicast Locator to advertise the transport parameters. +/// +/// The [`Join`] message flow is the following: +/// /// ```text -/// NOTE: 16 bits (2 bytes) may be prepended to the serialized message indicating the total length -/// in bytes of the message, resulting in the maximum length of a message being 65_535 bytes. -/// This is necessary in those stream-oriented transports (e.g., TCP) that do not preserve -/// the boundary of the serialized messages. The length is encoded as little-endian. -/// In any case, the length of a message must not exceed 65_535 bytes. +/// A B +/// | JOIN | +/// |------------------>| +/// | | +/// ... ... +/// | JOIN | +/// |------------------>| +/// | | +/// ``` /// -/// The JOIN message is sent on a multicast Locator to advertise the transport parameters. +/// The JOIN message structure is defined as follows: +/// +/// ```text +/// Flags: +/// - T: Lease period if T==1 then the lease period is in seconds else in milliseconds +/// - S: Size params If S==1 then size parameters are exchanged +/// - Z: Extensions If Z==1 then zenoh extensions will follow. /// /// 7 6 5 4 3 2 1 0 /// +-+-+-+-+-+-+-+-+ -/// |O|S|T| JOIN | -/// +-+-+-+-+-------+ -/// ~ |Q~ if O==1 +/// |Z|S|T| JOIN | +/// +-+-+-+---------+ +/// | version | /// +---------------+ -/// | v_maj | v_min | -- Protocol Version VMaj.VMin -/// +-------+-------+ -/// ~ whatami ~ -- Router, Peer or a combination of them +/// |zid_len|x|x|wai| (#)(*) +/// +-------+-+-+---+ +/// ~ [u8] ~ -- ZenohID of the sender of the INIT message /// +---------------+ -/// ~ peer_id ~ -- PID of the sender of the JOIN message +/// |x|x|x|x|rid|fsn| \ -- SN/ID resolution (+) +/// +---------------+ | if Flag(S)==1 +/// | u16 | | -- Batch Size ($) +/// | | / /// +---------------+ -/// ~ lease ~ -- Lease period of the sender of the JOIN message(*) +/// % lease % -- Lease period of the sender of the JOIN message /// +---------------+ -/// ~ sn_resolution ~ if S==1(*) -- Otherwise 2^28 is assumed(**) +/// % next_sn % -- Next SN to be sent by the sender of the JOIN(^) /// +---------------+ -/// ~ [next_sn] ~ (***) +/// ~ [JoinExts] ~ -- if Flag(Z)==1 /// +---------------+ /// -/// - if Q==1 then the sender supports QoS. +/// If A==1 and S==0 then size parameters are (ie. S flag) are accepted. +/// +/// (*) WhatAmI. It indicates the role of the zenoh node sending the JOIN message. +/// The valid WhatAmI values are: +/// - 0b00: Router +/// - 0b01: Peer +/// - 0b10: Client +/// - 0b11: Reserved +/// +/// (#) ZID length. It indicates how many bytes are used for the ZenohID bytes. +/// A ZenohID is minimum 1 byte and maximum 16 bytes. Therefore, the actual lenght is computed as: +/// real_zid_len := 1 + zid_len /// -/// (*) if T==1 then the lease period is expressed in seconds, otherwise in milliseconds -/// (**) if S==0 then 2^28 is assumed. -/// (***) if Q==1 then 8 sequence numbers are present: one for each priority. -/// if Q==0 then only one sequence number is present. +/// (+) Sequence Number/ID resolution. It indicates the resolution and consequently the wire overhead +/// of various SN and ID in Zenoh. +/// - fsn: frame/fragment sequence number resolution. Used in Frame/Fragment messages. +/// - rid: request ID resolution. Used in Request/Response messages. +/// The valid SN/ID resolution values are: +/// - 0b00: 8 bits +/// - 0b01: 16 bits +/// - 0b10: 32 bits +/// - 0b11: 64 bits /// +/// ($) Batch Size. It indicates the maximum size of a batch the sender of the JOIN message is willing +/// to accept when reading from the network. Default on multicast: 8192. +/// +/// (^) The next sequence number MUST be compatible with the adverstised Sequence Number resolution /// ``` +/// + +pub mod flag { + pub const T: u8 = 1 << 5; // 0x20 Lease period if T==1 then the lease period is in seconds else in milliseconds + pub const S: u8 = 1 << 6; // 0x40 Size params if S==1 then size parameters are advertised + pub const Z: u8 = 1 << 7; // 0x80 Extensions if Z==1 then an extension will follow +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct Join { pub version: u8, pub whatami: WhatAmI, pub zid: ZenohId, + pub resolution: Resolution, + pub batch_size: BatchSize, pub lease: Duration, - pub sn_resolution: ZInt, - pub next_sns: ConduitSnList, + pub next_sn: PrioritySn, + pub ext_qos: Option, + pub ext_shm: Option, } -impl Join { - pub fn is_qos(&self) -> bool { - match self.next_sns { - ConduitSnList::QoS(_) => true, - ConduitSnList::Plain(_) => false, - } - } +// Extensions +pub mod ext { + use super::{Priority, PrioritySn}; + use crate::{common::ZExtZBuf, zextzbuf}; + use alloc::boxed::Box; + + /// # QoS extension + /// Used to announce next sn when QoS is enabled + pub type QoS = zextzbuf!(0x1, true); + pub type QoSType = Box<[PrioritySn; Priority::NUM]>; + + /// # Shm extension + /// Used to advertise shared memory capabilities + pub type Shm = zextzbuf!(0x2, true); } impl Join { #[cfg(feature = "test")] pub fn rand() -> Self { - use crate::core::{ConduitSn, Priority}; + use crate::common::ZExtZBuf; use rand::Rng; let mut rng = rand::thread_rng(); @@ -82,33 +140,29 @@ impl Join { let version: u8 = rng.gen(); let whatami = WhatAmI::rand(); let zid = ZenohId::default(); + let resolution = Resolution::rand(); + let batch_size: u16 = rng.gen(); let lease = if rng.gen_bool(0.5) { Duration::from_secs(rng.gen()) } else { Duration::from_millis(rng.gen()) }; - let sn_resolution: ZInt = rng.gen(); - let next_sns = if rng.gen_bool(0.5) { - let mut sns = Box::new([ConduitSn::default(); Priority::NUM]); - for i in 0..Priority::NUM { - sns[i].reliable = rng.gen(); - sns[i].best_effort = rng.gen(); - } - ConduitSnList::QoS(sns) - } else { - ConduitSnList::Plain(ConduitSn { - reliable: rng.gen(), - best_effort: rng.gen(), - }) - }; + let next_sn = PrioritySn::rand(); + let ext_qos = rng + .gen_bool(0.5) + .then_some(Box::new([PrioritySn::rand(); Priority::NUM])); + let ext_shm = rng.gen_bool(0.5).then_some(ZExtZBuf::rand()); Self { version, whatami, zid, + resolution, + batch_size, lease, - sn_resolution, - next_sns, + next_sn, + ext_qos, + ext_shm, } } } diff --git a/commons/zenoh-protocol/src/transport/keepalive.rs b/commons/zenoh-protocol/src/transport/keepalive.rs index 065cb7d7e9..927b0cd46b 100644 --- a/commons/zenoh-protocol/src/transport/keepalive.rs +++ b/commons/zenoh-protocol/src/transport/keepalive.rs @@ -11,45 +11,82 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::core::ZenohId; /// # KeepAlive message /// +/// The [`KeepAlive`] message SHOULD be sent periodically to avoid the expiration of the +/// link lease period. A [`KeepAlive`] message MAY NOT be sent on the link if some other +/// data has been transmitted on the same link during the last keep alive interval. +/// +/// The [`KeepAlive`] message flow is the following: +/// /// ```text -/// NOTE: 16 bits (2 bytes) may be prepended to the serialized message indicating the total length -/// in bytes of the message, resulting in the maximum length of a message being 65_535 bytes. -/// This is necessary in those stream-oriented transports (e.g., TCP) that do not preserve -/// the boundary of the serialized messages. The length is encoded as little-endian. -/// In any case, the length of a message must not exceed 65_535 bytes. +/// A B +/// | KEEP ALIVE | +/// |------------------>| +/// | | +/// ~ ... ~ +/// | | +/// | KEEP ALIVE | +/// |<------------------| +/// | | +/// | KEEP ALIVE | +/// |------------------>| +/// | | +/// ~ ... ~ +/// | | +/// | KEEP ALIVE | +/// |<------------------| +/// | | +/// ~ ... ~ +/// | | +/// | KEEP ALIVE | +/// |------------------>| +/// | | +/// ~ ... ~ +/// | | +/// ``` +/// +/// NOTE: In order to consider eventual packet loss, transmission latency and jitter, the time +/// interval between two subsequent [`KeepAlive`] messages SHOULD be set to one fourth of +/// the lease time. This is in-line with the ITU-T G.8013/Y.1731 specification on continous +/// connectivity check which considers a link as failed when no messages are received in +/// 3.5 times the target keep alive interval. /// -/// The KEEP_ALIVE message can be sent periodically to avoid the expiration of the transport lease -/// period in case there are no messages to be sent. +/// The [`KeepAlive`] message structure is defined as follows: +/// +/// ```text +/// Flags: +/// - X: Reserved +/// - X: Reserved +/// - Z: Extensions If Z==1 then zenoh extensions will follow. /// /// 7 6 5 4 3 2 1 0 /// +-+-+-+-+-+-+-+-+ -/// |X|X|I| K_ALIVE | -/// +-+-+-+-+-------+ -/// ~ zenoh_id ~ if I==1 -- Zenoh ID of the KEEP_ALIVE sender. +/// |Z|X|X| KALIVE | +/// +-+-+-+---------+ +/// ~ [KAliveExts] ~ if Flag(Z)==1 /// +---------------+ /// ``` -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct KeepAlive { - pub zid: Option, +/// +/// NOTE: 16 bits (2 bytes) may be prepended to the serialized message indicating the total length +/// in bytes of the message, resulting in the maximum length of a message being 65535 bytes. +/// This is necessary in those stream-oriented transports (e.g., TCP) that do not preserve +/// the boundary of the serialized messages. The length is encoded as little-endian. +/// In any case, the length of a message must not exceed 65535 bytes. +/// +pub mod flag { + // pub const X: u8 = 1 << 5; // 0x20 Reserved + // pub const X: u8 = 1 << 6; // 0x40 Reserved + pub const Z: u8 = 1 << 7; // 0x80 Extensions if Z==1 then an extension will follow } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct KeepAlive; + impl KeepAlive { #[cfg(feature = "test")] pub fn rand() -> Self { - use rand::Rng; - - let mut rng = rand::thread_rng(); - - let zid = if rng.gen_bool(0.5) { - Some(ZenohId::default()) - } else { - None - }; - - Self { zid } + Self } } diff --git a/commons/zenoh-protocol/src/transport/mod.rs b/commons/zenoh-protocol/src/transport/mod.rs index 8989402ed2..5a1025fb36 100644 --- a/commons/zenoh-protocol/src/transport/mod.rs +++ b/commons/zenoh-protocol/src/transport/mod.rs @@ -11,119 +11,85 @@ // Contributors: // ZettaScale Zenoh Team, // -mod close; -mod frame; -mod init; -mod join; -mod keepalive; -mod open; - -use crate::{ - common::Attachment, - core::{Channel, ConduitSnList, WhatAmI, ZInt, ZenohId}, -}; -pub use close::*; -use core::time::Duration; -pub use frame::*; -pub use init::*; -pub use join::*; -pub use keepalive::*; -pub use open::*; -use zenoh_buffers::ZSlice; - -pub mod tmsg { - use crate::common::imsg; - use crate::core::{Priority, ZInt}; - - // Transport message IDs -- Re-export of some of the Inner Message IDs - pub mod id { - use super::imsg; - - // Messages - pub const SCOUT: u8 = imsg::id::SCOUT; - pub const HELLO: u8 = imsg::id::HELLO; - pub const INIT: u8 = imsg::id::INIT; - pub const OPEN: u8 = imsg::id::OPEN; - pub const CLOSE: u8 = imsg::id::CLOSE; - pub const SYNC: u8 = imsg::id::SYNC; - pub const ACK_NACK: u8 = imsg::id::ACK_NACK; - pub const KEEP_ALIVE: u8 = imsg::id::KEEP_ALIVE; - pub const PING_PONG: u8 = imsg::id::PING_PONG; - pub const FRAME: u8 = imsg::id::FRAME; - pub const JOIN: u8 = imsg::id::JOIN; - - // Message decorators - pub const PRIORITY: u8 = imsg::id::PRIORITY; - pub const ATTACHMENT: u8 = imsg::id::ATTACHMENT; - } +pub mod close; +pub mod fragment; +pub mod frame; +pub mod init; +pub mod join; +pub mod keepalive; +pub mod oam; +pub mod open; - // Transport message flags - pub mod flag { - pub const A: u8 = 1 << 5; // 0x20 Ack if A==1 then the message is an acknowledgment - pub const C: u8 = 1 << 6; // 0x40 Count if C==1 then number of unacknowledged messages is present - pub const E: u8 = 1 << 7; // 0x80 End if E==1 then it is the last FRAME fragment - pub const F: u8 = 1 << 6; // 0x40 Fragment if F==1 then the FRAME is a fragment - pub const I: u8 = 1 << 5; // 0x20 PeerID if I==1 then the PeerID is requested or present - pub const K: u8 = 1 << 6; // 0x40 CloseLink if K==1 then close the transport link only - pub const L: u8 = 1 << 7; // 0x80 Locators if L==1 then Locators are present - pub const M: u8 = 1 << 5; // 0x20 Mask if M==1 then a Mask is present - pub const O: u8 = 1 << 7; // 0x80 Options if O==1 then Options are present - pub const P: u8 = 1 << 5; // 0x20 PingOrPong if P==1 then the message is Ping, otherwise is Pong - pub const R: u8 = 1 << 5; // 0x20 Reliable if R==1 then it concerns the reliable channel, best-effort otherwise - pub const S: u8 = 1 << 6; // 0x40 SN Resolution if S==1 then the SN Resolution is present - pub const T1: u8 = 1 << 5; // 0x20 TimeRes if U==1 then the time resolution is in seconds - pub const T2: u8 = 1 << 6; // 0x40 TimeRes if T==1 then the time resolution is in seconds - pub const W: u8 = 1 << 6; // 0x40 WhatAmI if W==1 then WhatAmI is indicated - pub const Z: u8 = 1 << 5; // 0x20 MixedSlices if Z==1 then the payload contains a mix of raw and shm_info payload - - pub const X: u8 = 0; // Unused flags are set to zero - } +pub use close::Close; +pub use fragment::{Fragment, FragmentHeader}; +pub use frame::{Frame, FrameHeader}; +pub use init::{InitAck, InitSyn}; +pub use join::Join; +pub use keepalive::KeepAlive; +pub use oam::Oam; +pub use open::{OpenAck, OpenSyn}; - pub mod init_options { - use super::ZInt; +#[cfg(feature = "shared-memory")] +use crate::network::NetworkMessage; - pub const QOS: ZInt = 1 << 0; // 0x01 QoS if PRIORITY==1 then the transport supports QoS - } +/// NOTE: 16 bits (2 bytes) may be prepended to the serialized message indicating the total length +/// in bytes of the message, resulting in the maximum length of a message being 65_535 bytes. +/// This is necessary in those stream-oriented transports (e.g., TCP) that do not preserve +/// the boundary of the serialized messages. The length is encoded as little-endian. +/// In any case, the length of a message must not exceed 65_535 bytes. +pub type BatchSize = u16; - pub mod join_options { - use super::ZInt; +pub mod batch_size { + use super::BatchSize; - pub const QOS: ZInt = 1 << 0; // 0x01 QoS if PRIORITY==1 then the transport supports QoS - } + pub const UNICAST: BatchSize = BatchSize::MAX; + pub const MULTICAST: BatchSize = 8_192; +} - // Reason for the Close message - pub mod close_reason { - pub const GENERIC: u8 = 0x00; - pub const UNSUPPORTED: u8 = 0x01; - pub const INVALID: u8 = 0x02; - pub const MAX_SESSIONS: u8 = 0x03; - pub const MAX_LINKS: u8 = 0x04; - pub const EXPIRED: u8 = 0x05; - } +pub mod id { + // WARNING: it's crucial that these IDs do NOT collide with the IDs + // defined in `crate::network::id`. + pub const OAM: u8 = 0x00; + pub const INIT: u8 = 0x01; // For unicast communications only + pub const OPEN: u8 = 0x02; // For unicast communications only + pub const CLOSE: u8 = 0x03; + pub const KEEP_ALIVE: u8 = 0x04; + pub const FRAME: u8 = 0x05; + pub const FRAGMENT: u8 = 0x06; + pub const JOIN: u8 = 0x07; // For multicast communications only +} - pub fn close_reason_to_str(reason: u8) -> &'static str { - match reason { - close_reason::GENERIC => "GENERIC", - close_reason::UNSUPPORTED => "UNSUPPORTED", - close_reason::INVALID => "INVALID", - close_reason::MAX_SESSIONS => "MAX_SESSIONS", - close_reason::MAX_LINKS => "MAX_LINKS", - close_reason::EXPIRED => "EXPIRED", - _ => "UNKNOWN", - } - } +#[cfg(feature = "shared-memory")] +#[derive(Debug)] +pub struct TransportMessageShm { + pub body: TransportBodyShm, +} +#[cfg(feature = "shared-memory")] +#[derive(Debug)] +pub enum TransportBodyShm { + Close(Close), + KeepAlive(KeepAlive), + Network(Box), +} - pub mod conduit { - use super::{imsg, Priority}; - - pub const CONTROL: u8 = (Priority::Control as u8) << imsg::HEADER_BITS; - pub const REAL_TIME: u8 = (Priority::RealTime as u8) << imsg::HEADER_BITS; - pub const INTERACTIVE_HIGH: u8 = (Priority::InteractiveHigh as u8) << imsg::HEADER_BITS; - pub const INTERACTIVE_LOW: u8 = (Priority::InteractiveLow as u8) << imsg::HEADER_BITS; - pub const DATA_HIGH: u8 = (Priority::DataHigh as u8) << imsg::HEADER_BITS; - pub const DATA: u8 = (Priority::Data as u8) << imsg::HEADER_BITS; - pub const DATA_LOW: u8 = (Priority::DataLow as u8) << imsg::HEADER_BITS; - pub const BACKGROUND: u8 = (Priority::Background as u8) << imsg::HEADER_BITS; +pub type TransportSn = u32; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] +pub struct PrioritySn { + pub reliable: TransportSn, + pub best_effort: TransportSn, +} + +impl PrioritySn { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); + + Self { + reliable: rng.gen(), + best_effort: rng.gen(), + } } } @@ -134,192 +100,168 @@ pub enum TransportBody { InitAck(InitAck), OpenSyn(OpenSyn), OpenAck(OpenAck), - Join(Join), Close(Close), KeepAlive(KeepAlive), Frame(Frame), + Fragment(Fragment), + OAM(Oam), + Join(Join), } #[derive(Debug, Clone, PartialEq, Eq)] pub struct TransportMessage { pub body: TransportBody, - pub attachment: Option, #[cfg(feature = "stats")] pub size: Option, } impl TransportMessage { - pub fn make_init_syn( - version: u8, - whatami: WhatAmI, - zid: ZenohId, - sn_resolution: ZInt, - is_qos: bool, - attachment: Option, - ) -> TransportMessage { - TransportMessage { - body: TransportBody::InitSyn(InitSyn { - version, - whatami, - zid, - sn_resolution, - is_qos, - }), - attachment, + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + + let mut rng = rand::thread_rng(); + + let body = match rng.gen_range(0..10) { + 0 => TransportBody::InitSyn(InitSyn::rand()), + 1 => TransportBody::InitAck(InitAck::rand()), + 2 => TransportBody::OpenSyn(OpenSyn::rand()), + 3 => TransportBody::OpenAck(OpenAck::rand()), + 4 => TransportBody::Close(Close::rand()), + 5 => TransportBody::KeepAlive(KeepAlive::rand()), + 6 => TransportBody::Frame(Frame::rand()), + 7 => TransportBody::Fragment(Fragment::rand()), + 8 => TransportBody::OAM(Oam::rand()), + 9 => TransportBody::Join(Join::rand()), + _ => unreachable!(), + }; + + Self { + body, #[cfg(feature = "stats")] size: None, } } +} - pub fn make_init_ack( - whatami: WhatAmI, - zid: ZenohId, - sn_resolution: Option, - is_qos: bool, - cookie: ZSlice, - attachment: Option, - ) -> TransportMessage { - TransportMessage { - body: TransportBody::InitAck(InitAck { - whatami, - zid, - sn_resolution, - is_qos, - cookie, - }), - attachment, +impl From for TransportMessage { + fn from(body: TransportBody) -> Self { + Self { + body, #[cfg(feature = "stats")] size: None, } } +} - pub fn make_open_syn( - lease: Duration, - initial_sn: ZInt, - cookie: ZSlice, - attachment: Option, - ) -> TransportMessage { - TransportMessage { - body: TransportBody::OpenSyn(OpenSyn { - lease, - initial_sn, - cookie, - }), - attachment, - #[cfg(feature = "stats")] - size: None, - } +impl From for TransportMessage { + fn from(init_syn: InitSyn) -> Self { + TransportBody::InitSyn(init_syn).into() } +} - pub fn make_open_ack( - lease: Duration, - initial_sn: ZInt, - attachment: Option, - ) -> TransportMessage { - TransportMessage { - body: TransportBody::OpenAck(OpenAck { lease, initial_sn }), - attachment, - #[cfg(feature = "stats")] - size: None, - } +impl From for TransportMessage { + fn from(init_ack: InitAck) -> Self { + TransportBody::InitAck(init_ack).into() } +} - pub fn make_join( - version: u8, - whatami: WhatAmI, - zid: ZenohId, - lease: Duration, - sn_resolution: ZInt, - next_sns: ConduitSnList, - attachment: Option, - ) -> TransportMessage { - TransportMessage { - body: TransportBody::Join(Join { - version, - whatami, - zid, - lease, - sn_resolution, - next_sns, - }), - attachment, - #[cfg(feature = "stats")] - size: None, - } +impl From for TransportMessage { + fn from(open_syn: OpenSyn) -> Self { + TransportBody::OpenSyn(open_syn).into() } +} - pub fn make_close( - zid: Option, - reason: u8, - link_only: bool, - attachment: Option, - ) -> TransportMessage { - TransportMessage { - body: TransportBody::Close(Close { - zid, - reason, - link_only, - }), - attachment, - #[cfg(feature = "stats")] - size: None, - } +impl From for TransportMessage { + fn from(open_ack: OpenAck) -> Self { + TransportBody::OpenAck(open_ack).into() } +} - pub fn make_keep_alive( - zid: Option, - attachment: Option, - ) -> TransportMessage { - TransportMessage { - body: TransportBody::KeepAlive(KeepAlive { zid }), - attachment, - #[cfg(feature = "stats")] - size: None, - } +impl From for TransportMessage { + fn from(close: Close) -> Self { + TransportBody::Close(close).into() } +} - pub fn make_frame( - channel: Channel, - sn: ZInt, - payload: FramePayload, - attachment: Option, - ) -> TransportMessage { - TransportMessage { - body: TransportBody::Frame(Frame { - channel, - sn, - payload, - }), - attachment, - #[cfg(feature = "stats")] - size: None, - } +impl From for TransportMessage { + fn from(keep_alive: KeepAlive) -> Self { + TransportBody::KeepAlive(keep_alive).into() } +} - #[cfg(feature = "test")] - pub fn rand() -> Self { - use rand::Rng; +impl From for TransportMessage { + fn from(frame: Frame) -> Self { + TransportBody::Frame(frame).into() + } +} - let mut rng = rand::thread_rng(); +impl From for TransportMessage { + fn from(fragment: Fragment) -> Self { + TransportBody::Fragment(fragment).into() + } +} - let attachment = if rng.gen_bool(0.5) { - Some(Attachment::rand()) - } else { - None - }; +impl From for TransportMessage { + fn from(join: Join) -> Self { + TransportBody::Join(join).into() + } +} - let body = match rng.gen_range(0..8) { - 0 => TransportBody::InitSyn(InitSyn::rand()), - 1 => TransportBody::InitAck(InitAck::rand()), - 2 => TransportBody::OpenSyn(OpenSyn::rand()), - 3 => TransportBody::OpenAck(OpenAck::rand()), - 4 => TransportBody::Join(Join::rand()), - 5 => TransportBody::Close(Close::rand()), - 6 => TransportBody::KeepAlive(KeepAlive::rand()), - 7 => TransportBody::Frame(Frame::rand()), - _ => unreachable!(), - }; +pub mod ext { + use crate::{common::ZExtZ64, core::Priority}; + + /// 7 6 5 4 3 2 1 0 + /// +-+-+-+-+-+-+-+-+ + /// %0| rsv |prio % + /// +---------------+ + /// - prio: Priority class + #[repr(transparent)] + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub struct QoSType { + inner: u8, + } + + impl QoSType<{ ID }> { + pub const P_MASK: u8 = 0b00000111; + + pub const fn new(priority: Priority) -> Self { + Self { + inner: priority as u8, + } + } + + pub const fn priority(&self) -> Priority { + unsafe { core::mem::transmute(self.inner & Self::P_MASK) } + } + + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); + + let inner: u8 = rng.gen(); + Self { inner } + } + } + + impl Default for QoSType<{ ID }> { + fn default() -> Self { + Self::new(Priority::default()) + } + } + + impl From> for QoSType<{ ID }> { + fn from(ext: ZExtZ64<{ ID }>) -> Self { + Self { + inner: ext.value as u8, + } + } + } - Self { body, attachment } + impl From> for ZExtZ64<{ ID }> { + fn from(ext: QoSType<{ ID }>) -> Self { + ZExtZ64::new(ext.inner as u64) + } } } diff --git a/commons/zenoh-protocol/src/transport/oam.rs b/commons/zenoh-protocol/src/transport/oam.rs new file mode 100644 index 0000000000..9822139401 --- /dev/null +++ b/commons/zenoh-protocol/src/transport/oam.rs @@ -0,0 +1,80 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::common::ZExtBody; + +pub type OamId = u16; + +pub mod flag { + pub const T: u8 = 1 << 5; // 0x20 Transport + // pub const X: u8 = 1 << 6; // 0x40 Reserved + pub const Z: u8 = 1 << 7; // 0x80 Extensions if Z==1 then an extension will follow +} + +/// ```text +/// Flags: +/// - E |: Encoding The encoding of the extension +/// - E/ +/// - Z: Extension If Z==1 then at least one extension is present +/// +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// |Z|ENC| OAM | +/// +-+-+-+---------+ +/// ~ id:z16 ~ +/// +---------------+ +/// ~ [oam_exts] ~ +/// +---------------+ +/// % length % -- If ENC == u64 || ENC == ZBuf +/// +---------------+ +/// ~ [u8] ~ -- If ENC == ZBuf +/// +---------------+ +/// ``` +/// +/// Encoding: +/// - 0b00: Unit +/// - 0b01: u64 +/// - 0b10: ZBuf +/// - 0b11: Reserved +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Oam { + pub id: OamId, + pub body: ZExtBody, + pub ext_qos: ext::QoSType, +} + +pub mod ext { + use crate::{common::ZExtZ64, zextz64}; + + pub type QoS = zextz64!(0x1, true); + pub type QoSType = crate::transport::ext::QoSType<{ QoS::ID }>; +} + +impl Oam { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); + + let id: OamId = rng.gen(); + let payload = ZExtBody::rand(); + let ext_qos = ext::QoSType::rand(); + + Self { + id, + body: payload, + ext_qos, + } + } +} diff --git a/commons/zenoh-protocol/src/transport/open.rs b/commons/zenoh-protocol/src/transport/open.rs index f82080b596..5b4be0632b 100644 --- a/commons/zenoh-protocol/src/transport/open.rs +++ b/commons/zenoh-protocol/src/transport/open.rs @@ -11,47 +11,107 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::core::ZInt; +use crate::transport::TransportSn; use core::time::Duration; use zenoh_buffers::ZSlice; /// # Open message /// +/// After having succesfully complete the [`super::InitSyn`]-[`super::InitAck`] message exchange, +/// the OPEN message is sent on a link to finalize the initialization of the link and +/// associated transport with a zenoh node. +/// For convenience, we call [`OpenSyn`] and [`OpenAck`] an OPEN message with the A flag +/// is set to 0 and 1, respectively. +/// +/// The [`OpenSyn`]/[`OpenAck`] message flow is the following: +/// /// ```text -/// NOTE: 16 bits (2 bytes) may be prepended to the serialized message indicating the total length -/// in bytes of the message, resulting in the maximum length of a message being 65_535 bytes. -/// This is necessary in those stream-oriented transports (e.g., TCP) that do not preserve -/// the boundary of the serialized messages. The length is encoded as little-endian. -/// In any case, the length of a message must not exceed 65_535 bytes. +/// A B +/// | OPEN SYN | +/// |------------------>| +/// | | +/// | OPEN ACK | +/// |<------------------| +/// | | +/// ``` /// -/// The OPEN message is sent on a link to finally open an initialized transport with the peer. +/// ```text +/// Flags: +/// - A: Ack If A==0 then the message is an OpenSyn else it is an OpenAck +/// - T: Lease period if T==1 then the lease period is in seconds else in milliseconds +/// - Z: Extensions If Z==1 then zenoh extensions will follow. /// /// 7 6 5 4 3 2 1 0 /// +-+-+-+-+-+-+-+-+ -/// |X|T|A| OPEN | -/// +-+-+-+-+-------+ -/// ~ lease ~ -- Lease period of the sender of the OPEN message(*) +/// |Z|T|A| OPEN | +/// +-+-+-+---------+ +/// % lease % -- Lease period of the sender of the OPEN message /// +---------------+ -/// ~ initial_sn ~ -- Initial SN proposed by the sender of the OPEN(**) +/// % initial_sn % -- Initial SN proposed by the sender of the OPEN(*) /// +---------------+ -/// ~ cookie ~ if A==0(***) +/// ~ ~ if Flag(A)==0 (**) -- Cookie +/// +---------------+ +/// ~ [OpenExts] ~ if Flag(Z)==1 /// +---------------+ /// -/// (*) if T==1 then the lease period is expressed in seconds, otherwise in milliseconds -/// (**) the initial sequence number MUST be compatible with the sequence number resolution agreed in the -/// InitSyn/InitAck message exchange -/// (***) the cookie MUST be the same received in the INIT message with A==1 from the corresponding peer +/// (*) The initial sequence number MUST be compatible with the sequence number resolution agreed in the +/// [`super::InitSyn`]-[`super::InitAck`] message exchange +/// (**) The cookie MUST be the same received in the [`super::InitAck`]from the corresponding zenoh node /// ``` +/// +/// NOTE: 16 bits (2 bytes) may be prepended to the serialized message indicating the total length +/// in bytes of the message, resulting in the maximum length of a message being 65535 bytes. +/// This is necessary in those stream-oriented transports (e.g., TCP) that do not preserve +/// the boundary of the serialized messages. The length is encoded as little-endian. +/// In any case, the length of a message must not exceed 65535 bytes. +/// + +pub mod flag { + pub const A: u8 = 1 << 5; // 0x20 Ack if A==0 then the message is an InitSyn else it is an InitAck + pub const T: u8 = 1 << 6; // 0x40 Lease period if T==1 then the lease period is in seconds else in milliseconds + pub const Z: u8 = 1 << 7; // 0x80 Extensions if Z==1 then an extension will follow +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct OpenSyn { pub lease: Duration, - pub initial_sn: ZInt, + pub initial_sn: TransportSn, pub cookie: ZSlice, + pub ext_qos: Option, + pub ext_shm: Option, + pub ext_auth: Option, + pub ext_mlink: Option, +} + +// Extensions +pub mod ext { + use crate::{ + common::{ZExtUnit, ZExtZ64, ZExtZBuf}, + zextunit, zextz64, zextzbuf, + }; + + /// # QoS extension + /// Used to negotiate the use of QoS + pub type QoS = zextunit!(0x1, false); + + /// # Shm extension + /// Used as challenge for probing shared memory capabilities + pub type Shm = zextz64!(0x2, false); + + /// # Auth extension + /// Used as challenge for probing authentication rights + pub type Auth = zextzbuf!(0x3, false); + + /// # Multilink extension + /// Used as challenge for probing multilink capabilities + pub type MultiLinkSyn = zextzbuf!(0x4, false); + pub type MultiLinkAck = zextunit!(0x4, false); } impl OpenSyn { #[cfg(feature = "test")] pub fn rand() -> Self { + use crate::common::{ZExtUnit, ZExtZ64, ZExtZBuf}; use rand::Rng; const MIN: usize = 32; @@ -65,12 +125,21 @@ impl OpenSyn { Duration::from_millis(rng.gen()) }; - let initial_sn: ZInt = rng.gen(); + let initial_sn: TransportSn = rng.gen(); let cookie = ZSlice::rand(rng.gen_range(MIN..=MAX)); + let ext_qos = rng.gen_bool(0.5).then_some(ZExtUnit::rand()); + let ext_shm = rng.gen_bool(0.5).then_some(ZExtZ64::rand()); + let ext_auth = rng.gen_bool(0.5).then_some(ZExtZBuf::rand()); + let ext_mlink = rng.gen_bool(0.5).then_some(ZExtZBuf::rand()); + Self { lease, initial_sn, cookie, + ext_qos, + ext_shm, + ext_auth, + ext_mlink, } } } @@ -78,12 +147,17 @@ impl OpenSyn { #[derive(Debug, Clone, PartialEq, Eq)] pub struct OpenAck { pub lease: Duration, - pub initial_sn: ZInt, + pub initial_sn: TransportSn, + pub ext_qos: Option, + pub ext_shm: Option, + pub ext_auth: Option, + pub ext_mlink: Option, } impl OpenAck { #[cfg(feature = "test")] pub fn rand() -> Self { + use crate::common::{ZExtUnit, ZExtZ64, ZExtZBuf}; use rand::Rng; let mut rng = rand::thread_rng(); @@ -94,7 +168,19 @@ impl OpenAck { Duration::from_millis(rng.gen()) }; - let initial_sn: ZInt = rng.gen(); - Self { lease, initial_sn } + let initial_sn: TransportSn = rng.gen(); + let ext_qos = rng.gen_bool(0.5).then_some(ZExtUnit::rand()); + let ext_shm = rng.gen_bool(0.5).then_some(ZExtZ64::rand()); + let ext_auth = rng.gen_bool(0.5).then_some(ZExtZBuf::rand()); + let ext_mlink = rng.gen_bool(0.5).then_some(ZExtUnit::rand()); + + Self { + lease, + initial_sn, + ext_qos, + ext_shm, + ext_auth, + ext_mlink, + } } } diff --git a/commons/zenoh-protocol/src/zenoh/ack.rs b/commons/zenoh-protocol/src/zenoh/ack.rs new file mode 100644 index 0000000000..d40bf58791 --- /dev/null +++ b/commons/zenoh-protocol/src/zenoh/ack.rs @@ -0,0 +1,84 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::common::ZExtUnknown; +use alloc::vec::Vec; +use uhlc::Timestamp; + +/// # Ack message +/// +/// ```text +/// Flags: +/// - T: Timestamp If T==1 then the timestamp if present +/// - X: Reserved +/// - Z: Extension If Z==1 then at least one extension is present +/// +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// |Z|X|T| ACK | +/// +-+-+-+---------+ +/// ~ ts: ~ if T==1 +/// +---------------+ +/// ~ [err_exts] ~ if Z==1 +/// +---------------+ +/// ``` +pub mod flag { + pub const T: u8 = 1 << 5; // 0x20 Timestamp if T==0 then the timestamp if present + // pub const X: u8 = 1 << 6; // 0x40 Reserved + pub const Z: u8 = 1 << 7; // 0x80 Extensions if Z==1 then an extension will follow +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Ack { + pub timestamp: Option, + pub ext_sinfo: Option, + pub ext_unknown: Vec, +} + +pub mod ext { + use crate::{common::ZExtZBuf, zextzbuf}; + + /// # SourceInfo extension + /// Used to carry additional information about the source of data + pub type SourceInfo = zextzbuf!(0x1, false); + pub type SourceInfoType = crate::zenoh::ext::SourceInfoType<{ SourceInfo::ID }>; +} + +impl Ack { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use crate::{common::iext, core::ZenohId}; + use rand::Rng; + let mut rng = rand::thread_rng(); + + let timestamp = rng.gen_bool(0.5).then_some({ + let time = uhlc::NTP64(rng.gen()); + let id = uhlc::ID::try_from(ZenohId::rand().to_le_bytes()).unwrap(); + Timestamp::new(time, id) + }); + let ext_sinfo = rng.gen_bool(0.5).then_some(ext::SourceInfoType::rand()); + let mut ext_unknown = Vec::new(); + for _ in 0..rng.gen_range(0..4) { + ext_unknown.push(ZExtUnknown::rand2( + iext::mid(ext::SourceInfo::ID) + 1, + false, + )); + } + + Self { + timestamp, + ext_sinfo, + ext_unknown, + } + } +} diff --git a/commons/zenoh-protocol/src/zenoh/data.rs b/commons/zenoh-protocol/src/zenoh/data.rs deleted file mode 100644 index e7fed65b99..0000000000 --- a/commons/zenoh-protocol/src/zenoh/data.rs +++ /dev/null @@ -1,218 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use crate::core::{CongestionControl, Encoding, SampleKind, Timestamp, WireExpr, ZInt, ZenohId}; -use zenoh_buffers::ZBuf; - -/// # ReplyContext decorator -/// -/// ```text -/// The **ReplyContext** is a message decorator for either: -/// - the **Data** messages that result from a query -/// - or a **Unit** message in case the message is a -/// SOURCE_FINAL or REPLY_FINAL. -/// The **replier-id** (queryable id) is represented as a byte-array. -/// -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+-+-+-+-+-+ -/// |X|X|F| R_CTX | -/// +-+-+-+---------+ -/// ~ qid ~ -/// +---------------+ -/// ~ replier_id ~ if F==0 -/// +---------------+ -/// -/// - if F==1 then the message is a REPLY_FINAL -/// ``` -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ReplierInfo { - pub id: ZenohId, -} -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ReplyContext { - pub qid: ZInt, - pub replier: Option, -} - -impl ReplyContext { - // Note: id replier_id=None flag F is set, meaning it's a REPLY_FINAL - pub fn new(qid: ZInt, replier: Option) -> Self { - Self { qid, replier } - } - - pub fn is_final(&self) -> bool { - self.replier.is_none() - } -} - -impl ReplyContext { - #[cfg(feature = "test")] - pub fn rand() -> Self { - use rand::Rng; - - let mut rng = rand::thread_rng(); - - let qid: ZInt = rng.gen(); - let replier = if rng.gen_bool(0.5) { - Some(ReplierInfo { - id: ZenohId::default(), - }) - } else { - None - }; - - Self { qid, replier } - } -} - -/// # DataInfo -/// -/// DataInfo data structure is optionally included in Data messages -/// -/// ```text -/// -/// Options bits -/// - 0: Payload is sliced -/// - 1: Payload kind -/// - 2: Payload encoding -/// - 3: Payload timestamp -/// - 4: Reserved -/// - 5: Reserved -/// - 6: Reserved -/// - 7: Payload source_id -/// - 8: Payload source_sn -/// - 9-63: Reserved -/// -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+---------+ -/// ~ options ~ -/// +---------------+ -/// ~ kind ~ if options & (1 << 1) -/// +---------------+ -/// ~ encoding ~ if options & (1 << 2) -/// +---------------+ -/// ~ timestamp ~ if options & (1 << 3) -/// +---------------+ -/// ~ source_id ~ if options & (1 << 7) -/// +---------------+ -/// ~ source_sn ~ if options & (1 << 8) -/// +---------------+ -/// -/// - if options & (1 << 0) then the payload is sliced -/// -/// ``` -#[derive(Debug, Clone, PartialEq, Eq, Default)] -pub struct DataInfo { - #[cfg(feature = "shared-memory")] - pub sliced: bool, - pub kind: SampleKind, - pub encoding: Option, - pub timestamp: Option, - pub source_id: Option, - pub source_sn: Option, -} - -impl DataInfo { - #[cfg(feature = "test")] - pub fn rand() -> Self { - use rand::Rng; - - let mut rng = rand::thread_rng(); - - #[cfg(feature = "shared-memory")] - let sliced = rng.gen_bool(0.5); - let kind = SampleKind::try_from(rng.gen_range(0..=1)).unwrap(); - let encoding = rng.gen_bool(0.5).then(Encoding::rand); - let timestamp = rng.gen_bool(0.5).then(|| { - let time = uhlc::NTP64(rng.gen()); - let id = uhlc::ID::try_from(ZenohId::rand().to_le_bytes()).unwrap(); - Timestamp::new(time, id) - }); - let source_id = rng.gen_bool(0.5).then(ZenohId::rand); - let source_sn = rng.gen_bool(0.5).then(|| rng.gen()); - - Self { - #[cfg(feature = "shared-memory")] - sliced, - kind, - encoding, - timestamp, - source_id, - source_sn, - } - } -} - -/// # Data message -/// -/// ```text -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+-+-+-+-+-+ -/// |K|I|D| DATA | -/// +-+-+-+---------+ -/// ~ KeyExpr ~ if K==1 -- Only numerical id -/// +---------------+ -/// ~ DataInfo ~ if I==1 -/// +---------------+ -/// ~ Payload ~ -/// +---------------+ -/// -/// ``` -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Data { - pub key: WireExpr<'static>, - pub data_info: Option, - pub payload: ZBuf, - pub congestion_control: CongestionControl, - pub reply_context: Option, -} - -impl Data { - #[cfg(feature = "test")] - pub fn rand() -> Self { - use rand::Rng; - - const MIN: usize = 2; - const MAX: usize = 16; - - let mut rng = rand::thread_rng(); - - let key = WireExpr::rand(); - let data_info = if rng.gen_bool(0.5) { - Some(DataInfo::rand()) - } else { - None - }; - - let payload = ZBuf::rand(rng.gen_range(MIN..MAX)); - - let congestion_control = if rng.gen_bool(0.5) { - CongestionControl::Block - } else { - CongestionControl::Drop - }; - let reply_context = if rng.gen_bool(0.5) { - Some(ReplyContext::rand()) - } else { - None - }; - - Self { - key, - data_info, - payload, - congestion_control, - reply_context, - } - } -} diff --git a/commons/zenoh-protocol/src/zenoh/declare.rs b/commons/zenoh-protocol/src/zenoh/declare.rs deleted file mode 100644 index 13cb652ec5..0000000000 --- a/commons/zenoh-protocol/src/zenoh/declare.rs +++ /dev/null @@ -1,296 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use crate::core::{QueryableInfo, SubInfo, WireExpr, ZInt}; -use alloc::vec::Vec; - -/// ```text -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+-+-+-+-+-+ -/// |X|X|X| DECLARE | -/// +-+-+-+---------+ -/// ~ Num of Decl ~ -/// +---------------+ -/// ~ [Declaration] ~ -/// +---------------+ -/// ``` -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Declare { - pub declarations: Vec, -} - -impl Declare { - #[cfg(feature = "test")] - pub fn rand() -> Self { - use rand::Rng; - - let mut rng = rand::thread_rng(); - - let n: usize = rng.gen_range(1..16); - let declarations = (0..n) - .map(|_| Declaration::rand()) - .collect::>(); - - Self { declarations } - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum Declaration { - Resource(Resource), - ForgetResource(ForgetResource), - Publisher(Publisher), - ForgetPublisher(ForgetPublisher), - Subscriber(Subscriber), - ForgetSubscriber(ForgetSubscriber), - Queryable(Queryable), - ForgetQueryable(ForgetQueryable), -} - -impl Declaration { - #[cfg(feature = "test")] - pub fn rand() -> Self { - use rand::Rng; - - let mut rng = rand::thread_rng(); - - match rng.gen_range(0..8) { - 0 => Declaration::Resource(Resource::rand()), - 1 => Declaration::ForgetResource(ForgetResource::rand()), - 2 => Declaration::Publisher(Publisher::rand()), - 3 => Declaration::ForgetPublisher(ForgetPublisher::rand()), - 4 => Declaration::Subscriber(Subscriber::rand()), - 5 => Declaration::ForgetSubscriber(ForgetSubscriber::rand()), - 6 => Declaration::Queryable(Queryable::rand()), - 7 => Declaration::ForgetQueryable(ForgetQueryable::rand()), - _ => unreachable!(), - } - } -} - -/// ```text -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+-+-+-+-+-+ -/// |K|X|X| RESOURCE| -/// +---------------+ -/// ~ RID ~ -/// +---------------+ -/// ~ KeyExpr ~ if K==1 then key_expr has suffix -/// +---------------+ -/// ``` -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Resource { - pub expr_id: ZInt, - pub key: WireExpr<'static>, -} - -impl Resource { - #[cfg(feature = "test")] - pub fn rand() -> Self { - use rand::Rng; - - let mut rng = rand::thread_rng(); - - let expr_id: ZInt = rng.gen(); - let key = WireExpr::rand(); - - Self { expr_id, key } - } -} - -/// ```text -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+-+-+-+-+-+ -/// |X|X|X| F_RES | -/// +---------------+ -/// ~ RID ~ -/// +---------------+ -/// ``` -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ForgetResource { - pub expr_id: ZInt, -} - -impl ForgetResource { - #[cfg(feature = "test")] - pub fn rand() -> Self { - use rand::Rng; - - let mut rng = rand::thread_rng(); - - let expr_id: ZInt = rng.gen(); - - Self { expr_id } - } -} - -/// ```text -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+-+-+-+-+-+ -/// |K|X|X| PUB | -/// +---------------+ -/// ~ KeyExpr ~ if K==1 then key_expr has suffix -/// +---------------+ -/// ``` -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Publisher { - pub key: WireExpr<'static>, -} - -impl Publisher { - #[cfg(feature = "test")] - pub fn rand() -> Self { - let key = WireExpr::rand(); - - Self { key } - } -} - -/// ```text -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+-+-+-+-+-+ -/// |K|X|X| F_PUB | -/// +---------------+ -/// ~ KeyExpr ~ if K==1 then key_expr has suffix -/// +---------------+ -/// ``` -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ForgetPublisher { - pub key: WireExpr<'static>, -} - -impl ForgetPublisher { - #[cfg(feature = "test")] - pub fn rand() -> Self { - let key = WireExpr::rand(); - - Self { key } - } -} - -/// ```text -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+-+-+-+-+-+ -/// |K|S|R| SUB | R for Reliable -/// +---------------+ -/// ~ KeyExpr ~ if K==1 then key_expr has suffix -/// +---------------+ -/// | SubMode | if S==1. Otherwise: SubMode=Push -/// +---------------+ -/// ``` -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Subscriber { - pub key: WireExpr<'static>, - pub info: SubInfo, -} - -impl Subscriber { - #[cfg(feature = "test")] - pub fn rand() -> Self { - use crate::core::{Reliability, SubMode}; - use rand::Rng; - - let mut rng = rand::thread_rng(); - - let key = WireExpr::rand(); - let reliability = if rng.gen_bool(0.5) { - Reliability::Reliable - } else { - Reliability::BestEffort - }; - let mode = if rng.gen_bool(0.5) { - SubMode::Push - } else { - SubMode::Pull - }; - let info = SubInfo { reliability, mode }; - - Self { key, info } - } -} - -/// ```text -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+-+-+-+-+-+ -/// |K|X|X| F_SUB | -/// +---------------+ -/// ~ KeyExpr ~ if K==1 then key_expr has suffix -/// +---------------+ -/// ``` -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ForgetSubscriber { - pub key: WireExpr<'static>, -} - -impl ForgetSubscriber { - #[cfg(feature = "test")] - pub fn rand() -> Self { - let key = WireExpr::rand(); - - Self { key } - } -} - -/// ```text -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+-+-+-+-+-+ -/// |K|Q|X| QABLE | -/// +---------------+ -/// ~ KeyExpr ~ if K==1 then key_expr has suffix -/// +---------------+ -/// ~ QablInfo ~ if Q==1 -/// +---------------+ -/// ``` -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Queryable { - pub key: WireExpr<'static>, - pub info: QueryableInfo, -} - -impl Queryable { - #[cfg(feature = "test")] - pub fn rand() -> Self { - use rand::Rng; - - let mut rng = rand::thread_rng(); - - let key = WireExpr::rand(); - let complete: ZInt = rng.gen(); - let distance: ZInt = rng.gen(); - let info = QueryableInfo { complete, distance }; - - Self { key, info } - } -} - -/// ```text -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+-+-+-+-+-+ -/// |K|X|X| F_QABLE | -/// +---------------+ -/// ~ KeyExpr ~ if K==1 then key_expr has suffix -/// +---------------+ -/// ``` -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ForgetQueryable { - pub key: WireExpr<'static>, -} - -impl ForgetQueryable { - #[cfg(feature = "test")] - pub fn rand() -> Self { - let key = WireExpr::rand(); - - Self { key } - } -} diff --git a/commons/zenoh-protocol/src/zenoh/del.rs b/commons/zenoh-protocol/src/zenoh/del.rs new file mode 100644 index 0000000000..0de867ce51 --- /dev/null +++ b/commons/zenoh-protocol/src/zenoh/del.rs @@ -0,0 +1,84 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::common::ZExtUnknown; +use alloc::vec::Vec; +use uhlc::Timestamp; + +/// # Put message +/// +/// ```text +/// Flags: +/// - T: Timestamp If T==1 then the timestamp if present +/// - X: Reserved +/// - Z: Extension If Z==1 then at least one extension is present +/// +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// |Z|X|T| DEL | +/// +-+-+-+---------+ +/// ~ ts: ~ if T==1 +/// +---------------+ +/// ~ [del_exts] ~ if Z==1 +/// +---------------+ +/// ``` +pub mod flag { + pub const T: u8 = 1 << 5; // 0x20 Timestamp if T==0 then the timestamp if present + // pub const X: u8 = 1 << 6; // 0x40 Reserved + pub const Z: u8 = 1 << 7; // 0x80 Extensions if Z==1 then an extension will follow +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Del { + pub timestamp: Option, + pub ext_sinfo: Option, + pub ext_unknown: Vec, +} + +pub mod ext { + use crate::{common::ZExtZBuf, zextzbuf}; + + /// # SourceInfo extension + /// Used to carry additional information about the source of data + pub type SourceInfo = zextzbuf!(0x1, false); + pub type SourceInfoType = crate::zenoh::ext::SourceInfoType<{ SourceInfo::ID }>; +} + +impl Del { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use crate::{common::iext, core::ZenohId}; + use rand::Rng; + let mut rng = rand::thread_rng(); + + let timestamp = rng.gen_bool(0.5).then_some({ + let time = uhlc::NTP64(rng.gen()); + let id = uhlc::ID::try_from(ZenohId::rand().to_le_bytes()).unwrap(); + Timestamp::new(time, id) + }); + let ext_sinfo = rng.gen_bool(0.5).then_some(ext::SourceInfoType::rand()); + let mut ext_unknown = Vec::new(); + for _ in 0..rng.gen_range(0..4) { + ext_unknown.push(ZExtUnknown::rand2( + iext::mid(ext::SourceInfo::ID) + 1, + false, + )); + } + + Self { + timestamp, + ext_sinfo, + ext_unknown, + } + } +} diff --git a/commons/zenoh-protocol/src/zenoh/err.rs b/commons/zenoh-protocol/src/zenoh/err.rs new file mode 100644 index 0000000000..648efff441 --- /dev/null +++ b/commons/zenoh-protocol/src/zenoh/err.rs @@ -0,0 +1,101 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::common::ZExtUnknown; +use alloc::vec::Vec; +use uhlc::Timestamp; + +/// # Err message +/// +/// ```text +/// Flags: +/// - T: Timestamp If T==1 then the timestamp if present +/// - I: Infrastructure If I==1 then the error is related to the infrastructure else to the user +/// - Z: Extension If Z==1 then at least one extension is present +/// +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// |Z|I|T| ERR | +/// +-+-+-+---------+ +/// % code:z16 % +/// +---------------+ +/// ~ ts: ~ if T==1 +/// +---------------+ +/// ~ [err_exts] ~ if Z==1 +/// +---------------+ +/// ``` +pub mod flag { + pub const T: u8 = 1 << 5; // 0x20 Timestamp if T==0 then the timestamp if present + pub const I: u8 = 1 << 6; // 0x40 Infrastructure if I==1 then the error is related to the infrastructure else to the user + pub const Z: u8 = 1 << 7; // 0x80 Extensions if Z==1 then an extension will follow +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Err { + pub code: u16, + pub is_infrastructure: bool, + pub timestamp: Option, + pub ext_sinfo: Option, + pub ext_body: Option, + pub ext_unknown: Vec, +} + +pub mod ext { + use crate::{common::ZExtZBuf, zextzbuf}; + + /// # SourceInfo extension + /// Used to carry additional information about the source of data + pub type SourceInfo = zextzbuf!(0x1, false); + pub type SourceInfoType = crate::zenoh::ext::SourceInfoType<{ SourceInfo::ID }>; + + /// # ErrBody extension + /// Used to carry a body attached to the query + /// Shared Memory extension is automatically defined by ValueType extension if + /// #[cfg(feature = "shared-memory")] is defined. + pub type ErrBodyType = crate::zenoh::ext::ValueType<{ ZExtZBuf::<0x02>::id(false) }, 0x03>; +} + +impl Err { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use crate::{common::iext, core::ZenohId}; + use rand::Rng; + let mut rng = rand::thread_rng(); + + let code: u16 = rng.gen(); + let is_infrastructure = rng.gen_bool(0.5); + let timestamp = rng.gen_bool(0.5).then_some({ + let time = uhlc::NTP64(rng.gen()); + let id = uhlc::ID::try_from(ZenohId::rand().to_le_bytes()).unwrap(); + Timestamp::new(time, id) + }); + let ext_sinfo = rng.gen_bool(0.5).then_some(ext::SourceInfoType::rand()); + let ext_body = rng.gen_bool(0.5).then_some(ext::ErrBodyType::rand()); + let mut ext_unknown = Vec::new(); + for _ in 0..rng.gen_range(0..4) { + ext_unknown.push(ZExtUnknown::rand2( + iext::mid(ext::ErrBodyType::SID) + 1, + false, + )); + } + + Self { + code, + is_infrastructure, + timestamp, + ext_sinfo, + ext_body, + ext_unknown, + } + } +} diff --git a/commons/zenoh-protocol/src/zenoh/mod.rs b/commons/zenoh-protocol/src/zenoh/mod.rs index e523b3fa7e..740c7e8b0d 100644 --- a/commons/zenoh-protocol/src/zenoh/mod.rs +++ b/commons/zenoh-protocol/src/zenoh/mod.rs @@ -1,5 +1,5 @@ // -// Copyright (c) 2023 ZettaScale Technology +// Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at @@ -11,428 +11,249 @@ // Contributors: // ZettaScale Zenoh Team, // -mod data; -mod declare; -mod linkstate; -mod pull; -mod query; -mod routing; -mod unit; - -use crate::{ - common::Attachment, - core::{ - Channel, CongestionControl, ConsolidationMode, QueryTarget, Reliability, WireExpr, ZInt, - }, -}; -use alloc::{string::String, vec::Vec}; -use core::fmt; -pub use data::*; -pub use declare::*; -pub use linkstate::*; -pub use pull::*; -pub use query::*; -pub use routing::*; -pub use unit::*; -use zenoh_buffers::ZBuf; - -pub mod zmsg { - use crate::{ - common::imsg, - core::{Channel, CongestionControl, Priority, Reliability, ZInt}, - }; - - // Zenoh message IDs -- Re-export of some of the Inner Message IDs - pub mod id { - use super::imsg; - - // Messages - pub const DECLARE: u8 = imsg::id::DECLARE; - pub const DATA: u8 = imsg::id::DATA; - pub const QUERY: u8 = imsg::id::QUERY; - pub const PULL: u8 = imsg::id::PULL; - pub const UNIT: u8 = imsg::id::UNIT; - pub const LINK_STATE_LIST: u8 = imsg::id::LINK_STATE_LIST; - - // Message decorators - pub const PRIORITY: u8 = imsg::id::PRIORITY; - pub const REPLY_CONTEXT: u8 = imsg::id::REPLY_CONTEXT; - pub const ATTACHMENT: u8 = imsg::id::ATTACHMENT; - pub const ROUTING_CONTEXT: u8 = imsg::id::ROUTING_CONTEXT; - } +pub mod ack; +pub mod del; +pub mod err; +pub mod pull; +pub mod put; +pub mod query; +pub mod reply; + +use crate::core::Encoding; +pub use ack::Ack; +pub use del::Del; +pub use err::Err; +pub use pull::Pull; +pub use put::Put; +pub use query::{Consolidation, Query}; +pub use reply::Reply; + +pub mod id { + pub const OAM: u8 = 0x00; + pub const PUT: u8 = 0x01; + pub const DEL: u8 = 0x02; + pub const QUERY: u8 = 0x03; + pub const REPLY: u8 = 0x04; + pub const ERR: u8 = 0x05; + pub const ACK: u8 = 0x06; + pub const PULL: u8 = 0x07; +} - // Zenoh message flags - pub mod flag { - pub const B: u8 = 1 << 6; // 0x40 QueryBody if B==1 then QueryBody is present - pub const D: u8 = 1 << 5; // 0x20 Drop if D==1 then the message can be dropped - pub const F: u8 = 1 << 5; // 0x20 Final if F==1 then this is the final message (e.g., ReplyContext, Pull) - pub const I: u8 = 1 << 6; // 0x40 DataInfo if I==1 then DataInfo is present - pub const K: u8 = 1 << 7; // 0x80 KeySuffix if K==1 then key_expr has suffix - pub const N: u8 = 1 << 6; // 0x40 MaxSamples if N==1 then the MaxSamples is indicated - pub const P: u8 = 1 << 0; // 0x01 Zid if P==1 then the zid is present - pub const Q: u8 = 1 << 6; // 0x40 QueryableInfo if Q==1 then the queryable info is present - pub const R: u8 = 1 << 5; // 0x20 Reliable if R==1 then it concerns the reliable channel, best-effort otherwise - pub const S: u8 = 1 << 6; // 0x40 SubMode if S==1 then the declaration SubMode is indicated - pub const T: u8 = 1 << 5; // 0x20 QueryTAK if T==1 then the query target is present - - pub const X: u8 = 0; // Unused flags are set to zero - } +// DataInfo +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DataInfo { + pub encoding: Encoding, +} - // Options used for DataInfo - pub mod data { - use super::ZInt; +// Push +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum PushBody { + Put(Put), + Del(Del), +} + +impl PushBody { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; - pub mod info { - use super::ZInt; + let mut rng = rand::thread_rng(); - #[cfg(feature = "shared-memory")] - pub const SLICED: ZInt = 1 << 0; // 0x01 - pub const KIND: ZInt = 1 << 1; // 0x02 - pub const ENCODING: ZInt = 1 << 2; // 0x04 - pub const TIMESTAMP: ZInt = 1 << 3; // 0x08 - // 0x10: Reserved - // 0x20: Reserved - // 0x40: Reserved - pub const SRCID: ZInt = 1 << 7; // 0x80 - pub const SRCSN: ZInt = 1 << 8; // 0x100 + match rng.gen_range(0..2) { + 0 => PushBody::Put(Put::rand()), + 1 => PushBody::Del(Del::rand()), + _ => unreachable!(), } } +} - pub mod declaration { - pub mod id { - // Declarations - pub const RESOURCE: u8 = 0x01; - pub const PUBLISHER: u8 = 0x02; - pub const SUBSCRIBER: u8 = 0x03; - pub const QUERYABLE: u8 = 0x04; - - pub const FORGET_RESOURCE: u8 = 0x11; - pub const FORGET_PUBLISHER: u8 = 0x12; - pub const FORGET_SUBSCRIBER: u8 = 0x13; - pub const FORGET_QUERYABLE: u8 = 0x14; - - // SubModes - pub const MODE_PUSH: u8 = 0x00; - pub const MODE_PULL: u8 = 0x01; - } +impl From for PushBody { + fn from(p: Put) -> PushBody { + PushBody::Put(p) + } +} - pub mod flag { - pub const PERIOD: u8 = 0x80; - } +impl From for PushBody { + fn from(d: Del) -> PushBody { + PushBody::Del(d) } +} + +// Request +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum RequestBody { + Query(Query), + Put(Put), + Del(Del), + Pull(Pull), +} + +impl RequestBody { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; - // Options used for LinkState - pub mod link_state { - use super::ZInt; + let mut rng = rand::thread_rng(); - pub const PID: ZInt = 1; // 0x01 - pub const WAI: ZInt = 1 << 1; // 0x02 - pub const LOC: ZInt = 1 << 2; // 0x04 + match rng.gen_range(0..3) { + 0 => RequestBody::Query(Query::rand()), + 1 => RequestBody::Put(Put::rand()), + 2 => RequestBody::Del(Del::rand()), + _ => unreachable!(), + } } +} - pub mod conduit { - use super::{imsg, Priority}; - - pub const CONTROL: u8 = (Priority::Control as u8) << imsg::HEADER_BITS; - pub const REAL_TIME: u8 = (Priority::RealTime as u8) << imsg::HEADER_BITS; - pub const INTERACTIVE_HIGH: u8 = (Priority::InteractiveHigh as u8) << imsg::HEADER_BITS; - pub const INTERACTIVE_LOW: u8 = (Priority::InteractiveLow as u8) << imsg::HEADER_BITS; - pub const DATA_HIGH: u8 = (Priority::DataHigh as u8) << imsg::HEADER_BITS; - pub const DATA: u8 = (Priority::Data as u8) << imsg::HEADER_BITS; - pub const DATA_LOW: u8 = (Priority::DataLow as u8) << imsg::HEADER_BITS; - pub const BACKGROUND: u8 = (Priority::Background as u8) << imsg::HEADER_BITS; +impl From for RequestBody { + fn from(q: Query) -> RequestBody { + RequestBody::Query(q) } +} - // Default reliability for each Zenoh Message - pub mod default_channel { - use super::{Channel, Priority, Reliability}; - - pub const DECLARE: Channel = Channel { - priority: Priority::RealTime, - reliability: Reliability::Reliable, - }; - pub const DATA: Channel = Channel { - priority: Priority::Data, - reliability: Reliability::BestEffort, - }; - pub const QUERY: Channel = Channel { - priority: Priority::Data, - reliability: Reliability::Reliable, - }; - pub const PULL: Channel = Channel { - priority: Priority::Data, - reliability: Reliability::Reliable, - }; - pub const REPLY: Channel = Channel { - priority: Priority::Data, - reliability: Reliability::Reliable, - }; - pub const UNIT: Channel = Channel { - priority: Priority::Data, - reliability: Reliability::BestEffort, - }; - pub const LINK_STATE_LIST: Channel = Channel { - priority: Priority::Control, - reliability: Reliability::Reliable, - }; +impl From for RequestBody { + fn from(p: Put) -> RequestBody { + RequestBody::Put(p) } +} - // Default congestion control for each Zenoh Message - pub mod default_congestion_control { - use super::CongestionControl; - - pub const DECLARE: CongestionControl = CongestionControl::Block; - pub const DATA: CongestionControl = CongestionControl::Drop; - pub const QUERY: CongestionControl = CongestionControl::Block; - pub const PULL: CongestionControl = CongestionControl::Block; - pub const REPLY: CongestionControl = CongestionControl::Block; - pub const UNIT: CongestionControl = CongestionControl::Block; - pub const LINK_STATE_LIST: CongestionControl = CongestionControl::Block; +impl From for RequestBody { + fn from(d: Del) -> RequestBody { + RequestBody::Del(d) } } -// Zenoh messages at zenoh level -#[allow(clippy::large_enum_variant)] +// Response #[derive(Debug, Clone, PartialEq, Eq)] -pub enum ZenohBody { - Data(Data), - Unit(Unit), - Pull(Pull), - Query(Query), - Declare(Declare), - LinkStateList(LinkStateList), +pub enum ResponseBody { + Reply(Reply), + Err(Err), + Ack(Ack), + Put(Put), } -#[derive(Clone, PartialEq, Eq)] -pub struct ZenohMessage { - pub body: ZenohBody, - pub channel: Channel, - pub routing_context: Option, - pub attachment: Option, - #[cfg(feature = "stats")] - pub size: Option, -} +impl ResponseBody { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; -impl ZenohMessage { - pub fn make_declare( - declarations: Vec, - routing_context: Option, - attachment: Option, - ) -> ZenohMessage { - ZenohMessage { - body: ZenohBody::Declare(Declare { declarations }), - channel: zmsg::default_channel::DECLARE, - routing_context, - attachment, - #[cfg(feature = "stats")] - size: None, - } - } + let mut rng = rand::thread_rng(); - #[allow(clippy::too_many_arguments)] - #[inline(always)] - pub fn make_data( - key: WireExpr<'static>, - payload: ZBuf, - channel: Channel, - congestion_control: CongestionControl, - data_info: Option, - routing_context: Option, - reply_context: Option, - attachment: Option, - ) -> ZenohMessage { - ZenohMessage { - body: ZenohBody::Data(Data { - key, - data_info, - payload, - congestion_control, - reply_context, - }), - channel, - routing_context, - attachment, - #[cfg(feature = "stats")] - size: None, + match rng.gen_range(0..4) { + 0 => ResponseBody::Reply(Reply::rand()), + 1 => ResponseBody::Err(Err::rand()), + 2 => ResponseBody::Ack(Ack::rand()), + 3 => ResponseBody::Put(Put::rand()), + _ => unreachable!(), } } +} - pub fn make_unit( - channel: Channel, - congestion_control: CongestionControl, - reply_context: Option, - attachment: Option, - ) -> ZenohMessage { - ZenohMessage { - body: ZenohBody::Unit(Unit { - congestion_control, - reply_context, - }), - channel, - routing_context: None, - attachment, - #[cfg(feature = "stats")] - size: None, - } +impl From for ResponseBody { + fn from(r: Reply) -> ResponseBody { + ResponseBody::Reply(r) } +} - pub fn make_pull( - is_final: bool, - key: WireExpr<'static>, - pull_id: ZInt, - max_samples: Option, - attachment: Option, - ) -> ZenohMessage { - ZenohMessage { - body: ZenohBody::Pull(Pull { - key, - pull_id, - max_samples, - is_final, - }), - channel: zmsg::default_channel::PULL, - routing_context: None, - attachment, - #[cfg(feature = "stats")] - size: None, - } +impl From for ResponseBody { + fn from(r: Err) -> ResponseBody { + ResponseBody::Err(r) } +} - #[allow(clippy::too_many_arguments)] - #[inline(always)] - pub fn make_query( - key: WireExpr<'static>, - parameters: String, - qid: ZInt, - target: Option, - consolidation: ConsolidationMode, - body: Option, - routing_context: Option, - attachment: Option, - ) -> ZenohMessage { - ZenohMessage { - body: ZenohBody::Query(Query { - key, - parameters, - qid, - target, - consolidation, - body, - }), - channel: zmsg::default_channel::QUERY, - routing_context, - attachment, - #[cfg(feature = "stats")] - size: None, - } +impl From for ResponseBody { + fn from(r: Ack) -> ResponseBody { + ResponseBody::Ack(r) } +} - pub fn make_link_state_list( - link_states: Vec, - attachment: Option, - ) -> ZenohMessage { - ZenohMessage { - body: ZenohBody::LinkStateList(LinkStateList { link_states }), - channel: zmsg::default_channel::LINK_STATE_LIST, - routing_context: None, - attachment, - #[cfg(feature = "stats")] - size: None, - } +pub mod ext { + use zenoh_buffers::ZBuf; + + use crate::core::{Encoding, ZenohId}; + + /// 7 6 5 4 3 2 1 0 + /// +-+-+-+-+-+-+-+-+ + /// |zid_len|X|X|X|X| + /// +-------+-+-+---+ + /// ~ zid ~ + /// +---------------+ + /// % eid % -- Counter decided by the Zenoh Node + /// +---------------+ + /// % sn % + /// +---------------+ + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct SourceInfoType { + pub zid: ZenohId, + pub eid: u32, + pub sn: u32, } - // -- Message Predicates - #[inline] - pub fn is_reliable(&self) -> bool { - self.channel.reliability == Reliability::Reliable - } + impl SourceInfoType<{ ID }> { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); - #[inline] - pub fn is_droppable(&self) -> bool { - if !self.is_reliable() { - return true; + let zid = ZenohId::rand(); + let eid: u32 = rng.gen(); + let sn: u32 = rng.gen(); + Self { zid, eid, sn } } - - let cc = match &self.body { - ZenohBody::Data(data) => data.congestion_control, - ZenohBody::Unit(unit) => unit.congestion_control, - ZenohBody::Declare(_) => zmsg::default_congestion_control::DECLARE, - ZenohBody::Pull(_) => zmsg::default_congestion_control::PULL, - ZenohBody::Query(_) => zmsg::default_congestion_control::QUERY, - ZenohBody::LinkStateList(_) => zmsg::default_congestion_control::LINK_STATE_LIST, - }; - - cc == CongestionControl::Drop } -} -impl fmt::Debug for ZenohMessage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "{:?} {:?} {:?} {:?}", - self.body, self.channel, self.routing_context, self.attachment - )?; - #[cfg(feature = "stats")] - write!(f, " {:?}", self.size)?; - Ok(()) - } -} + /// 7 6 5 4 3 2 1 0 + /// +-+-+-+-+-+-+-+-+ + /// +-+-+-+-+-+-+-+-+ + #[cfg(feature = "shared-memory")] + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct ShmType; + + #[cfg(feature = "shared-memory")] + impl ShmType<{ ID }> { + pub const fn new() -> Self { + Self + } -impl fmt::Display for ZenohMessage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(self, f) + #[cfg(feature = "test")] + pub const fn rand() -> Self { + Self + } } -} -impl ZenohMessage { - #[cfg(feature = "test")] - pub fn rand() -> Self { - use crate::core::Priority; - use rand::Rng; + /// 7 6 5 4 3 2 1 0 + /// +-+-+-+-+-+-+-+-+ + /// ~ encoding ~ + /// +---------------+ + /// ~ pl: [u8;z32] ~ -- Payload + /// +---------------+ + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct ValueType { + #[cfg(feature = "shared-memory")] + pub ext_shm: Option>, + pub encoding: Encoding, + pub payload: ZBuf, + } - let mut rng = rand::thread_rng(); + impl ValueType<{ VID }, { SID }> { + pub const VID: u8 = VID; + pub const SID: u8 = SID; - let attachment = if rng.gen_bool(0.5) { - Some(Attachment::rand()) - } else { - None - }; - - let routing_context = if rng.gen_bool(0.5) { - Some(RoutingContext::rand()) - } else { - None - }; - - let priority: Priority = rng - .gen_range(Priority::MAX as u8..=Priority::MIN as u8) - .try_into() - .unwrap(); - let reliability = if rng.gen_bool(0.5) { - Reliability::Reliable - } else { - Reliability::BestEffort - }; - let channel = Channel { - priority, - reliability, - }; - let body = match rng.gen_range(0..6) { - 0 => ZenohBody::Data(Data::rand()), - 1 => ZenohBody::Unit(Unit::rand()), - 2 => ZenohBody::Pull(Pull::rand()), - 3 => ZenohBody::Query(Query::rand()), - 4 => ZenohBody::Declare(Declare::rand()), - 5 => ZenohBody::LinkStateList(LinkStateList::rand()), - _ => unreachable!(), - }; + #[cfg(feature = "test")] + pub fn rand() -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); - Self { - body, - channel, - routing_context, - attachment, + #[cfg(feature = "shared-memory")] + let ext_shm = rng.gen_bool(0.5).then_some(ShmType::rand()); + let encoding = Encoding::rand(); + let payload = ZBuf::rand(rng.gen_range(1..=64)); + + Self { + #[cfg(feature = "shared-memory")] + ext_shm, + encoding, + payload, + } } } } diff --git a/commons/zenoh-protocol/src/zenoh/pull.rs b/commons/zenoh-protocol/src/zenoh/pull.rs index 94c465b66e..eb4f7eb55e 100644 --- a/commons/zenoh-protocol/src/zenoh/pull.rs +++ b/commons/zenoh-protocol/src/zenoh/pull.rs @@ -1,5 +1,5 @@ // -// Copyright (c) 2023 ZettaScale Technology +// Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at @@ -11,51 +11,46 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::core::{WireExpr, ZInt}; +use crate::common::ZExtUnknown; +use alloc::vec::Vec; /// # Pull message /// /// ```text -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+-+-+-+-+-+ -/// |K|N|F| PULL | -/// +-+-+-+---------+ -/// ~ KeyExpr ~ if K==1 then key_expr has suffix -/// +---------------+ -/// ~ pullid ~ -/// +---------------+ -/// ~ max_samples ~ if N==1 -/// +---------------+ +/// Flags: +/// - X: Reserved +/// - X: Reserved +/// - Z: Extension If Z==1 then at least one extension is present +/// +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// |Z|X|X| PULL | +/// +-+-+-+---------+ +/// ~ [pull_exts] ~ if Z==1 +/// +---------------+ /// ``` +pub mod flag { + // pub const X: u8 = 1 << 5; // 0x20 Reserved + // pub const X: u8 = 1 << 6; // 0x40 Reserved + pub const Z: u8 = 1 << 7; // 0x80 Extensions if Z==1 then an extension will follow +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct Pull { - pub key: WireExpr<'static>, - pub pull_id: ZInt, - pub max_samples: Option, - pub is_final: bool, + pub ext_unknown: Vec, } impl Pull { #[cfg(feature = "test")] pub fn rand() -> Self { use rand::Rng; - let mut rng = rand::thread_rng(); - let key = WireExpr::rand(); - let pull_id: ZInt = rng.gen(); - let max_samples = if rng.gen_bool(0.5) { - Some(rng.gen()) - } else { - None - }; - let is_final = rng.gen_bool(0.5); - - Self { - key, - pull_id, - max_samples, - is_final, + let mut ext_unknown = Vec::new(); + for _ in 0..rng.gen_range(0..4) { + ext_unknown.push(ZExtUnknown::rand2(1, false)); } + + Self { ext_unknown } } } diff --git a/commons/zenoh-protocol/src/zenoh/put.rs b/commons/zenoh-protocol/src/zenoh/put.rs new file mode 100644 index 0000000000..30b8ef837a --- /dev/null +++ b/commons/zenoh-protocol/src/zenoh/put.rs @@ -0,0 +1,110 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::{common::ZExtUnknown, core::Encoding}; +use alloc::vec::Vec; +use uhlc::Timestamp; +use zenoh_buffers::ZBuf; + +/// # Put message +/// +/// ```text +/// Flags: +/// - T: Timestamp If T==1 then the timestamp if present +/// - E: Encoding If E==1 then the encoding is present +/// - Z: Extension If Z==1 then at least one extension is present +/// +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// |Z|E|T| PUT | +/// +-+-+-+---------+ +/// ~ ts: ~ if T==1 +/// +---------------+ +/// ~ encoding ~ if E==1 +/// +---------------+ +/// ~ [put_exts] ~ if Z==1 +/// +---------------+ +/// ~ pl: ~ -- Payload +/// +---------------+ +/// ``` +pub mod flag { + pub const T: u8 = 1 << 5; // 0x20 Timestamp if T==0 then the timestamp if present + pub const E: u8 = 1 << 6; // 0x40 Encoding if E==1 then the encoding is present + pub const Z: u8 = 1 << 7; // 0x80 Extensions if Z==1 then an extension will follow +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Put { + pub timestamp: Option, + pub encoding: Encoding, + pub ext_sinfo: Option, + #[cfg(feature = "shared-memory")] + pub ext_shm: Option, + pub ext_unknown: Vec, + pub payload: ZBuf, +} + +pub mod ext { + #[cfg(feature = "shared-memory")] + use crate::{common::ZExtUnit, zextunit}; + use crate::{common::ZExtZBuf, zextzbuf}; + + /// # SourceInfo extension + /// Used to carry additional information about the source of data + pub type SourceInfo = zextzbuf!(0x1, false); + pub type SourceInfoType = crate::zenoh::ext::SourceInfoType<{ SourceInfo::ID }>; + + /// # Shared Memory extension + /// Used to carry additional information about the shared-memory layour of data + #[cfg(feature = "shared-memory")] + pub type Shm = zextunit!(0x2, true); + #[cfg(feature = "shared-memory")] + pub type ShmType = crate::zenoh::ext::ShmType<{ Shm::ID }>; +} + +impl Put { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use crate::{common::iext, core::ZenohId}; + use rand::Rng; + let mut rng = rand::thread_rng(); + + let timestamp = rng.gen_bool(0.5).then_some({ + let time = uhlc::NTP64(rng.gen()); + let id = uhlc::ID::try_from(ZenohId::rand().to_le_bytes()).unwrap(); + Timestamp::new(time, id) + }); + let encoding = Encoding::rand(); + let ext_sinfo = rng.gen_bool(0.5).then_some(ext::SourceInfoType::rand()); + #[cfg(feature = "shared-memory")] + let ext_shm = rng.gen_bool(0.5).then_some(ext::ShmType::rand()); + let mut ext_unknown = Vec::new(); + for _ in 0..rng.gen_range(0..4) { + ext_unknown.push(ZExtUnknown::rand2( + iext::mid(ext::SourceInfo::ID) + 1, + false, + )); + } + let payload = ZBuf::rand(rng.gen_range(1..=64)); + + Self { + timestamp, + encoding, + ext_sinfo, + #[cfg(feature = "shared-memory")] + ext_shm, + ext_unknown, + payload, + } + } +} diff --git a/commons/zenoh-protocol/src/zenoh/query.rs b/commons/zenoh-protocol/src/zenoh/query.rs index cb084dcf2e..17a2aa1d59 100644 --- a/commons/zenoh-protocol/src/zenoh/query.rs +++ b/commons/zenoh-protocol/src/zenoh/query.rs @@ -1,5 +1,5 @@ // -// Copyright (c) 2023 ZettaScale Technology +// Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at @@ -11,136 +11,150 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::{ - core::{ConsolidationMode, QueryTarget, WireExpr, ZInt}, - zenoh::DataInfo, -}; -use alloc::string::String; -use zenoh_buffers::ZBuf; - -/// # QueryBody -/// -/// QueryBody data structure is optionally included in Query messages -/// -/// ```text -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+---------+ -/// ~ DataInfo ~ -/// +---------------+ -/// ~ Payload ~ -/// +---------------+ -/// ``` -#[derive(Debug, Clone, PartialEq, Eq, Default)] -pub struct QueryBody { - pub data_info: DataInfo, - pub payload: ZBuf, +use crate::{common::ZExtUnknown, core::ConsolidationMode}; +use alloc::{string::String, vec::Vec}; + +/// The kind of consolidation. +#[repr(u8)] +#[derive(Debug, Default, Clone, PartialEq, Eq, Copy)] +pub enum Consolidation { + /// Apply automatic consolidation based on queryable's preferences + #[default] + Auto, + /// No consolidation applied: multiple samples may be received for the same key-timestamp. + None, + /// Monotonic consolidation immediately forwards samples, except if one with an equal or more recent timestamp + /// has already been sent with the same key. + /// + /// This optimizes latency while potentially reducing bandwidth. + /// + /// Note that this doesn't cause re-ordering, but drops the samples for which a more recent timestamp has already + /// been observed with the same key. + Monotonic, + /// Holds back samples to only send the set of samples that had the highest timestamp for their key. + Latest, + /// Remove the duplicates of any samples based on the their timestamp. + Unique, } -impl QueryBody { +impl Consolidation { #[cfg(feature = "test")] pub fn rand() -> Self { - use rand::Rng; - - const MIN: usize = 2; - const MAX: usize = 16; - + use rand::prelude::SliceRandom; let mut rng = rand::thread_rng(); - let data_info = DataInfo::rand(); - let payload = ZBuf::rand(rng.gen_range(MIN..MAX)); + *[ + Self::None, + Self::Monotonic, + Self::Latest, + Self::Unique, + Self::Auto, + ] + .choose(&mut rng) + .unwrap() + } +} - Self { data_info, payload } +impl From for Consolidation { + fn from(val: ConsolidationMode) -> Self { + match val { + ConsolidationMode::None => Consolidation::None, + ConsolidationMode::Monotonic => Consolidation::Monotonic, + ConsolidationMode::Latest => Consolidation::Latest, + } } } /// # Query message /// /// ```text -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+-+-+-+-+-+ -/// |K|B|T| QUERY | -/// +-+-+-+---------+ -/// ~ KeyExpr ~ if K==1 then key_expr has suffix -/// +---------------+ -/// ~selector_params~ -/// +---------------+ -/// ~ qid ~ -/// +---------------+ -/// ~ target ~ if T==1 -/// +---------------+ -/// ~ consolidation ~ -/// +---------------+ -/// ~ QueryBody ~ if B==1 -/// +---------------+ +/// Flags: +/// - P: Parameters If P==1 then the parameters are present +/// - X: Reserved +/// - Z: Extension If Z==1 then at least one extension is present +/// +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// |Z|X|P| QUERY | +/// +-+-+-+---------+ +/// ~ ps: ~ if P==1 +/// +---------------+ +/// ~ [qry_exts] ~ if Z==1 +/// +---------------+ +/// ``` +pub mod flag { + pub const P: u8 = 1 << 5; // 0x20 Parameters if P==1 then the parameters are present + // pub const X: u8 = 1 << 6; // 0x40 Reserved + pub const Z: u8 = 1 << 7; // 0x80 Extensions if Z==1 then an extension will follow +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct Query { - pub key: WireExpr<'static>, pub parameters: String, - pub qid: ZInt, - pub target: Option, - pub consolidation: ConsolidationMode, - pub body: Option, + pub ext_sinfo: Option, + pub ext_consolidation: Consolidation, + pub ext_body: Option, + pub ext_unknown: Vec, +} + +pub mod ext { + use crate::{ + common::{ZExtZ64, ZExtZBuf}, + zextz64, zextzbuf, + }; + + /// # SourceInfo extension + /// Used to carry additional information about the source of data + pub type SourceInfo = zextzbuf!(0x1, false); + pub type SourceInfoType = crate::zenoh::ext::SourceInfoType<{ SourceInfo::ID }>; + + /// # Consolidation extension + pub type Consolidation = zextz64!(0x2, true); + pub type ConsolidationType = crate::zenoh::query::Consolidation; + + /// # QueryBody extension + /// Used to carry a body attached to the query + /// Shared Memory extension is automatically defined by ValueType extension if + /// #[cfg(feature = "shared-memory")] is defined. + pub type QueryBodyType = crate::zenoh::ext::ValueType<{ ZExtZBuf::<0x03>::id(false) }, 0x04>; } impl Query { #[cfg(feature = "test")] pub fn rand() -> Self { + use crate::common::iext; use rand::{ distributions::{Alphanumeric, DistString}, - seq::SliceRandom, Rng, }; + let mut rng = rand::thread_rng(); const MIN: usize = 2; const MAX: usize = 16; - let mut rng = rand::thread_rng(); - - let key = WireExpr::rand(); - - let parameters = if rng.gen_bool(0.5) { + let parameters: String = if rng.gen_bool(0.5) { let len = rng.gen_range(MIN..MAX); Alphanumeric.sample_string(&mut rng, len) } else { String::new() }; - - let qid: ZInt = rng.gen(); - - let target = if rng.gen_bool(0.5) { - let t = [ - QueryTarget::All, - QueryTarget::AllComplete, - QueryTarget::BestMatching, - #[cfg(feature = "complete_n")] - QueryTarget::Complete(rng.gen()), - ]; - let t = t.choose(&mut rng).unwrap(); - Some(*t) - } else { - None - }; - let consolidation = *[ - ConsolidationMode::Latest, - ConsolidationMode::Monotonic, - ConsolidationMode::None, - ] - .choose(&mut rng) - .unwrap(); - - let body = if rng.gen_bool(0.5) { - Some(QueryBody::rand()) - } else { - None - }; + let ext_sinfo = rng.gen_bool(0.5).then_some(ext::SourceInfoType::rand()); + let ext_consolidation = Consolidation::rand(); + let ext_body = rng.gen_bool(0.5).then_some(ext::QueryBodyType::rand()); + let mut ext_unknown = Vec::new(); + for _ in 0..rng.gen_range(0..4) { + ext_unknown.push(ZExtUnknown::rand2( + iext::mid(ext::QueryBodyType::SID) + 1, + false, + )); + } Self { - key, parameters, - qid, - target, - consolidation, - body, + ext_sinfo, + ext_consolidation, + ext_body, + ext_unknown, } } } diff --git a/commons/zenoh-protocol/src/zenoh/reply.rs b/commons/zenoh-protocol/src/zenoh/reply.rs new file mode 100644 index 0000000000..d6b65f88c0 --- /dev/null +++ b/commons/zenoh-protocol/src/zenoh/reply.rs @@ -0,0 +1,120 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::{common::ZExtUnknown, core::Encoding}; +use alloc::vec::Vec; +use uhlc::Timestamp; +use zenoh_buffers::ZBuf; + +/// # Reply message +/// +/// ```text +/// Flags: +/// - T: Timestamp If T==1 then the timestamp if present +/// - E: Encoding If E==1 then the encoding is present +/// - Z: Extension If Z==1 then at least one extension is present +/// +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// |Z|E|T| REPLY | +/// +-+-+-+---------+ +/// ~ ts: ~ if T==1 +/// +---------------+ +/// ~ encoding ~ if E==1 +/// +---------------+ +/// ~ [repl_exts] ~ if Z==1 +/// +---------------+ +/// ~ pl: ~ -- Payload +/// +---------------+ +/// ``` +pub mod flag { + pub const T: u8 = 1 << 5; // 0x20 Timestamp if T==0 then the timestamp if present + pub const E: u8 = 1 << 6; // 0x40 Encoding if E==1 then the encoding is present + pub const Z: u8 = 1 << 7; // 0x80 Extensions if Z==1 then an extension will follow +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Reply { + pub timestamp: Option, + pub encoding: Encoding, + pub ext_sinfo: Option, + pub ext_consolidation: ext::ConsolidationType, + #[cfg(feature = "shared-memory")] + pub ext_shm: Option, + pub ext_unknown: Vec, + pub payload: ZBuf, +} + +pub mod ext { + #[cfg(feature = "shared-memory")] + use crate::{common::ZExtUnit, zextunit}; + use crate::{ + common::{ZExtZ64, ZExtZBuf}, + zextz64, zextzbuf, + }; + + /// # SourceInfo extension + /// Used to carry additional information about the source of data + pub type SourceInfo = zextzbuf!(0x1, false); + pub type SourceInfoType = crate::zenoh::ext::SourceInfoType<{ SourceInfo::ID }>; + + /// # Consolidation extension + pub type Consolidation = zextz64!(0x2, true); + pub type ConsolidationType = crate::zenoh::query::ext::ConsolidationType; + + /// # Shared Memory extension + /// Used to carry additional information about the shared-memory layour of data + #[cfg(feature = "shared-memory")] + pub type Shm = zextunit!(0x3, true); + #[cfg(feature = "shared-memory")] + pub type ShmType = crate::zenoh::ext::ShmType<{ Shm::ID }>; +} + +impl Reply { + #[cfg(feature = "test")] + pub fn rand() -> Self { + use crate::{common::iext, core::ZenohId, zenoh::Consolidation}; + use rand::Rng; + let mut rng = rand::thread_rng(); + + let timestamp = rng.gen_bool(0.5).then_some({ + let time = uhlc::NTP64(rng.gen()); + let id = uhlc::ID::try_from(ZenohId::rand().to_le_bytes()).unwrap(); + Timestamp::new(time, id) + }); + let encoding = Encoding::rand(); + let ext_sinfo = rng.gen_bool(0.5).then_some(ext::SourceInfoType::rand()); + let ext_consolidation = Consolidation::rand(); + #[cfg(feature = "shared-memory")] + let ext_shm = rng.gen_bool(0.5).then_some(ext::ShmType::rand()); + let mut ext_unknown = Vec::new(); + for _ in 0..rng.gen_range(0..4) { + ext_unknown.push(ZExtUnknown::rand2( + iext::mid(ext::Consolidation::ID) + 1, + false, + )); + } + let payload = ZBuf::rand(rng.gen_range(1..=64)); + + Self { + timestamp, + encoding, + ext_sinfo, + ext_consolidation, + #[cfg(feature = "shared-memory")] + ext_shm, + ext_unknown, + payload, + } + } +} diff --git a/commons/zenoh-protocol/src/zenoh/routing.rs b/commons/zenoh-protocol/src/zenoh/routing.rs deleted file mode 100644 index 92aadf49d4..0000000000 --- a/commons/zenoh-protocol/src/zenoh/routing.rs +++ /dev/null @@ -1,49 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use crate::core::ZInt; - -/// -- RoutingContext decorator -/// -/// ```text -/// The **RoutingContext** is a message decorator containing -/// informations for routing the concerned message. -/// -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+-+-+-+-+-+ -/// |X|X|X| RT_CTX | -/// +-+-+-+---------+ -/// ~ tid ~ -/// +---------------+ -/// ``` -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct RoutingContext { - pub tree_id: ZInt, -} - -impl RoutingContext { - pub fn new(tree_id: ZInt) -> RoutingContext { - RoutingContext { tree_id } - } -} - -impl RoutingContext { - #[cfg(feature = "test")] - pub fn rand() -> Self { - use rand::Rng; - - let mut rng = rand::thread_rng(); - - Self { tree_id: rng.gen() } - } -} diff --git a/commons/zenoh-protocol/src/zenoh/unit.rs b/commons/zenoh-protocol/src/zenoh/unit.rs deleted file mode 100644 index 3f23baa2e6..0000000000 --- a/commons/zenoh-protocol/src/zenoh/unit.rs +++ /dev/null @@ -1,54 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use super::ReplyContext; -use crate::core::CongestionControl; - -/// # Unit message -/// -/// ```text -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+-+-+-+-+-+ -/// |X|X|D| UNIT | -/// +-+-+-+---------+ -/// -/// ``` -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Unit { - pub congestion_control: CongestionControl, - pub reply_context: Option, -} - -impl Unit { - #[cfg(feature = "test")] - pub fn rand() -> Self { - use rand::Rng; - - let mut rng = rand::thread_rng(); - - let congestion_control = if rng.gen_bool(0.5) { - CongestionControl::Block - } else { - CongestionControl::Drop - }; - let reply_context = if rng.gen_bool(0.5) { - Some(ReplyContext::rand()) - } else { - None - }; - Self { - congestion_control, - reply_context, - } - } -} diff --git a/commons/zenoh-shm/src/lib.rs b/commons/zenoh-shm/src/lib.rs index 139795eae3..f76d4c4f92 100644 --- a/commons/zenoh-shm/src/lib.rs +++ b/commons/zenoh-shm/src/lib.rs @@ -11,7 +11,6 @@ // Contributors: // ZettaScale Zenoh Team, // -use serde::{Deserialize, Serialize}; use shared_memory::{Shmem, ShmemConf, ShmemError}; use std::{ any::Any, @@ -21,7 +20,7 @@ use std::{ sync::atomic::{AtomicPtr, AtomicUsize, Ordering}, }; use zenoh_buffers::ZSliceBuffer; -use zenoh_result::{bail, zerror, ShmError, ZResult}; +use zenoh_result::{zerror, ShmError, ZResult}; const MIN_FREE_CHUNK_SIZE: usize = 1_024; const ACCOUNTED_OVERHEAD: usize = 4_096; @@ -67,7 +66,7 @@ impl PartialEq for Chunk { /// /// This that can be serialized and can be used to retrieve the [`SharedMemoryBuf`] in a remote process. #[non_exhaustive] -#[derive(Serialize, Deserialize, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct SharedMemoryBufInfo { /// The index of the beginning of the buffer in the shm segment. pub offset: usize, @@ -90,31 +89,6 @@ impl SharedMemoryBufInfo { } } -impl SharedMemoryBufInfo { - pub fn serialize(&self) -> ZResult> { - bincode::serialize(self) - .map_err(|e| zerror!("Unable to serialize SharedMemoryBufInfo: {}", e).into()) - } - - pub fn deserialize(bs: &[u8]) -> ZResult { - match bincode::deserialize::(bs) { - Ok(info) => Ok(info), - Err(e) => bail!("Unable to deserialize SharedMemoryBufInfo: {}", e), - } - } -} - -impl Clone for SharedMemoryBufInfo { - fn clone(&self) -> SharedMemoryBufInfo { - SharedMemoryBufInfo { - shm_manager: self.shm_manager.clone(), - kind: self.kind, - offset: self.offset, - length: self.length, - } - } -} - /// A zenoh buffer in shared memory. #[non_exhaustive] pub struct SharedMemoryBuf { @@ -525,43 +499,6 @@ impl fmt::Debug for SharedMemoryManager { } // Buffer impls -// - SharedMemoryBufInfoSerialized -#[derive(Debug)] -#[repr(transparent)] -pub struct SharedMemoryBufInfoSerialized(Vec); - -impl AsRef<[u8]> for SharedMemoryBufInfoSerialized { - fn as_ref(&self) -> &[u8] { - self.0.as_slice() - } -} - -impl AsMut<[u8]> for SharedMemoryBufInfoSerialized { - fn as_mut(&mut self) -> &mut [u8] { - self.0.as_mut_slice() - } -} - -impl ZSliceBuffer for SharedMemoryBufInfoSerialized { - fn as_slice(&self) -> &[u8] { - self.as_ref() - } - - fn as_mut_slice(&mut self) -> &mut [u8] { - self.as_mut() - } - - fn as_any(&self) -> &dyn Any { - self - } -} - -impl From> for SharedMemoryBufInfoSerialized { - fn from(v: Vec) -> Self { - Self(v) - } -} - // - SharedMemoryBuf impl AsRef<[u8]> for SharedMemoryBuf { fn as_ref(&self) -> &[u8] { diff --git a/commons/zenoh-sync/src/object_pool.rs b/commons/zenoh-sync/src/object_pool.rs index 6b277d97da..d26bc7ea7c 100644 --- a/commons/zenoh-sync/src/object_pool.rs +++ b/commons/zenoh-sync/src/object_pool.rs @@ -142,11 +142,9 @@ impl ZSliceBuffer for RecyclingObject> { fn as_slice(&self) -> &[u8] { self.as_ref() } - fn as_mut_slice(&mut self) -> &mut [u8] { self.as_mut() } - fn as_any(&self) -> &dyn Any { self } diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 3f6fcbafe3..a07febd43f 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -28,6 +28,16 @@ readme = "README.md" [features] shared-memory = ["zenoh/shared-memory"] unstable = ["zenoh/unstable"] +transport_shm = ["zenoh/transport_shm"] + +# Unfortunately, the feature "transport_shm" is always +# enabled for the lines below. It looks like a Cargo bug :( +# +# [target.'cfg(unix)'.dependencies] +# zenoh = { workspace = true, features = ["transport_shm"] } +# +# [target.'cfg(not(unix))'.dependencies] +# zenoh = { workspace = true } [dependencies] async-std = { workspace = true, features = ["attributes"] } diff --git a/examples/examples/z_ping.rs b/examples/examples/z_ping.rs index 2eadbc02d2..173d270a83 100644 --- a/examples/examples/z_ping.rs +++ b/examples/examples/z_ping.rs @@ -12,6 +12,8 @@ // ZettaScale Zenoh Team, // use clap::{App, Arg}; +#[cfg(not(feature = "shared-memory"))] +use std::process::exit; use std::time::{Duration, Instant}; use zenoh::config::Config; use zenoh::prelude::sync::*; @@ -98,6 +100,7 @@ fn parse_args() -> (Config, Duration, usize, usize) { .arg(Arg::from_usage( "--no-multicast-scouting 'Disable the multicast-based scouting mechanism.'", )) + .arg(Arg::from_usage("--enable-shm 'Enable SHM transport.'")) .arg(Arg::from_usage( "-c, --config=[FILE] 'A configuration file.'", )) @@ -123,6 +126,16 @@ fn parse_args() -> (Config, Duration, usize, usize) { if args.is_present("no-multicast-scouting") { config.scouting.multicast.set_enabled(Some(false)).unwrap(); } + if args.is_present("enable-shm") { + #[cfg(feature = "shared-memory")] + config.transport.shared_memory.set_enabled(true).unwrap(); + #[cfg(not(feature = "shared-memory"))] + { + println!("enable-shm argument: SHM cannot be enabled, because Zenoh is compiled without shared-memory feature!"); + exit(-1); + } + } + let n: usize = args.value_of("samples").unwrap().parse().unwrap(); let w: f64 = args.value_of("warmup").unwrap().parse().unwrap(); let size: usize = args.value_of("PAYLOAD_SIZE").unwrap().parse().unwrap(); diff --git a/examples/examples/z_pong.rs b/examples/examples/z_pong.rs index 273245ccac..d1c7bbb867 100644 --- a/examples/examples/z_pong.rs +++ b/examples/examples/z_pong.rs @@ -14,6 +14,8 @@ use std::io::{stdin, Read}; // ZettaScale Zenoh Team, // use clap::{App, Arg}; +#[cfg(not(feature = "shared-memory"))] +use std::process::exit; use zenoh::config::Config; use zenoh::prelude::sync::*; use zenoh::publication::CongestionControl; @@ -61,6 +63,7 @@ fn parse_args() -> Config { .arg(Arg::from_usage( "--no-multicast-scouting 'Disable the multicast-based scouting mechanism.'", )) + .arg(Arg::from_usage("--enable-shm 'Enable SHM transport.'")) .arg(Arg::from_usage( "-c, --config=[FILE] 'A configuration file.'", )) @@ -83,6 +86,15 @@ fn parse_args() -> Config { if args.is_present("no-multicast-scouting") { config.scouting.multicast.set_enabled(Some(false)).unwrap(); } + if args.is_present("enable-shm") { + #[cfg(feature = "shared-memory")] + config.transport.shared_memory.set_enabled(true).unwrap(); + #[cfg(not(feature = "shared-memory"))] + { + println!("enable-shm argument: SHM cannot be enabled, because Zenoh is compiled without shared-memory feature!"); + exit(-1); + } + } config } diff --git a/examples/examples/z_pub_thr.rs b/examples/examples/z_pub_thr.rs index 89c7e5db29..b761128f3f 100644 --- a/examples/examples/z_pub_thr.rs +++ b/examples/examples/z_pub_thr.rs @@ -13,6 +13,8 @@ // use clap::{App, Arg}; use std::convert::TryInto; +#[cfg(not(feature = "shared-memory"))] +use std::process::exit; use zenoh::config::Config; use zenoh::prelude::sync::*; use zenoh::publication::CongestionControl; @@ -82,6 +84,7 @@ fn parse_args() -> (Config, usize, Priority, bool, usize) { .arg(Arg::from_usage( "--no-multicast-scouting 'Disable the multicast-based scouting mechanism.'", )) + .arg(Arg::from_usage("--enable-shm 'Enable SHM transport.'")) .arg(Arg::from_usage( " 'Sets the size of the payload to publish'", )) @@ -106,9 +109,19 @@ fn parse_args() -> (Config, usize, Priority, bool, usize) { if let Some(values) = args.values_of("listen") { config.listen.endpoints = values.map(|v| v.parse().unwrap()).collect(); } + if args.is_present("no-multicast-scouting") { config.scouting.multicast.set_enabled(Some(false)).unwrap(); } + if args.is_present("enable-shm") { + #[cfg(feature = "shared-memory")] + config.transport.shared_memory.set_enabled(true).unwrap(); + #[cfg(not(feature = "shared-memory"))] + { + println!("enable-shm argument: SHM cannot be enabled, because Zenoh is compiled without shared-memory feature!"); + exit(-1); + } + } let number: usize = args.value_of("number").unwrap().parse().unwrap(); diff --git a/examples/examples/z_sub_thr.rs b/examples/examples/z_sub_thr.rs index f77e2c1ef4..3db5a82203 100644 --- a/examples/examples/z_sub_thr.rs +++ b/examples/examples/z_sub_thr.rs @@ -13,6 +13,8 @@ // use clap::{App, Arg}; use std::io::{stdin, Read}; +#[cfg(not(feature = "shared-memory"))] +use std::process::exit; use std::time::Instant; use zenoh::config::Config; use zenoh::prelude::sync::*; @@ -119,6 +121,7 @@ fn parse_args() -> (Config, usize, usize) { .arg(Arg::from_usage( "-c, --config=[FILE] 'A configuration file.'", )) + .arg(Arg::from_usage("--enable-shm 'Enable SHM transport.'")) .arg(Arg::from_usage( "--no-multicast-scouting 'Disable the multicast-based scouting mechanism.'", )) @@ -141,6 +144,15 @@ fn parse_args() -> (Config, usize, usize) { if args.is_present("no-multicast-scouting") { config.scouting.multicast.set_enabled(Some(false)).unwrap(); } + if args.is_present("enable-shm") { + #[cfg(feature = "shared-memory")] + config.transport.shared_memory.set_enabled(true).unwrap(); + #[cfg(not(feature = "shared-memory"))] + { + println!("enable-shm argument: SHM cannot be enabled, because Zenoh is compiled without shared-memory feature!"); + exit(-1); + } + } let samples: usize = args.value_of("samples").unwrap().parse().unwrap(); let number: usize = args.value_of("number").unwrap().parse().unwrap(); diff --git a/io/zenoh-link-commons/Cargo.toml b/io/zenoh-link-commons/Cargo.toml index 1aca7ce702..51db4d671c 100644 --- a/io/zenoh-link-commons/Cargo.toml +++ b/io/zenoh-link-commons/Cargo.toml @@ -31,7 +31,6 @@ flume = { workspace = true } serde = { workspace = true, features = ["default"] } typenum = { workspace = true } zenoh-buffers = { workspace = true } -zenoh-cfg-properties = { workspace = true } zenoh-codec = { workspace = true } zenoh-protocol = { workspace = true } zenoh-result = { workspace = true } diff --git a/io/zenoh-link-commons/src/lib.rs b/io/zenoh-link-commons/src/lib.rs index 230547c7ed..114990726a 100644 --- a/io/zenoh-link-commons/src/lib.rs +++ b/io/zenoh-link-commons/src/lib.rs @@ -17,28 +17,20 @@ //! This crate is intended for Zenoh's internal use. //! //! [Click here for Zenoh's documentation](../zenoh/index.html) +#![no_std] +extern crate alloc; + +mod multicast; +mod unicast; + +use alloc::{borrow::ToOwned, boxed::Box, string::String}; use async_trait::async_trait; +use core::{cmp::PartialEq, fmt, hash::Hash}; +pub use multicast::*; use serde::Serialize; -use std::{ - borrow::Cow, - cmp::PartialEq, - convert::TryFrom, - fmt, - hash::{Hash, Hasher}, - ops::Deref, - sync::Arc, -}; -use zenoh_buffers::{ - reader::{HasReader, Reader}, - writer::{HasWriter, Writer}, -}; -use zenoh_cfg_properties::Properties; -use zenoh_codec::{RCodec, WCodec, Zenoh060}; -use zenoh_protocol::{ - core::{EndPoint, Locator}, - transport::TransportMessage, -}; -use zenoh_result::{zerror, ZResult}; +pub use unicast::*; +use zenoh_protocol::core::Locator; +use zenoh_result::ZResult; /*************************************/ /* GENERAL */ @@ -58,9 +50,10 @@ pub trait LocatorInspector: Default { fn protocol(&self) -> &str; async fn is_multicast(&self, locator: &Locator) -> ZResult; } + #[async_trait] pub trait ConfigurationInspector: Default { - async fn inspect_config(&self, configuration: &C) -> ZResult; + async fn inspect_config(&self, configuration: &C) -> ZResult; } impl fmt::Display for Link { @@ -106,267 +99,3 @@ impl From for Link { Link::from(&link) } } - -/*************************************/ -/* UNICAST */ -/*************************************/ - -pub type LinkManagerUnicast = Arc; -#[async_trait] -pub trait LinkManagerUnicastTrait: Send + Sync { - async fn new_link(&self, endpoint: EndPoint) -> ZResult; - async fn new_listener(&self, endpoint: EndPoint) -> ZResult; - async fn del_listener(&self, endpoint: &EndPoint) -> ZResult<()>; - fn get_listeners(&self) -> Vec; - fn get_locators(&self) -> Vec; -} -pub type NewLinkChannelSender = flume::Sender; -pub trait ConstructibleLinkManagerUnicast: Sized { - fn new(new_link_sender: NewLinkChannelSender, config: T) -> ZResult; -} - -#[derive(Clone, PartialEq, Eq)] -pub enum LinkUnicastDirection { - Inbound, - Outbound, -} - -#[derive(Clone)] -pub struct LinkUnicast(pub Arc); - -#[async_trait] -pub trait LinkUnicastTrait: Send + Sync { - fn get_mtu(&self) -> u16; - fn get_src(&self) -> &Locator; - fn get_dst(&self) -> &Locator; - fn is_reliable(&self) -> bool; - fn is_streamed(&self) -> bool; - async fn write(&self, buffer: &[u8]) -> ZResult; - async fn write_all(&self, buffer: &[u8]) -> ZResult<()>; - async fn read(&self, buffer: &mut [u8]) -> ZResult; - async fn read_exact(&self, buffer: &mut [u8]) -> ZResult<()>; - async fn close(&self) -> ZResult<()>; -} - -impl LinkUnicast { - pub async fn write_transport_message(&self, msg: &TransportMessage) -> ZResult { - const ERR: &str = "Write error on link: "; - - // Create the buffer for serializing the message - let mut buff = vec![]; - let mut writer = buff.writer(); - let codec = Zenoh060::default(); - - // Reserve 16 bits to write the length - if self.is_streamed() { - writer - .write_exact(u16::MIN.to_le_bytes().as_slice()) - .map_err(|_| zerror!("{ERR}{self}"))?; - } - // Serialize the message - codec - .write(&mut writer, msg) - .map_err(|_| zerror!("{ERR}{self}"))?; - - // Write the length - if self.is_streamed() { - let num = u16::MIN.to_le_bytes().len(); - let len = u16::try_from(writer.len() - num).map_err(|_| zerror!("{ERR}{self}"))?; - buff[..num].copy_from_slice(len.to_le_bytes().as_slice()); - } - - // Send the message on the link - self.0.write_all(buff.as_slice()).await?; - - Ok(buff.len()) - } - - pub async fn read_transport_message(&self) -> ZResult> { - // Read from the link - let buffer = if self.is_streamed() { - // Read and decode the message length - let mut length_bytes = [0_u8; 2]; - self.read_exact(&mut length_bytes).await?; - let to_read = u16::from_le_bytes(length_bytes) as usize; - // Read the message - let mut buffer = zenoh_buffers::vec::uninit(to_read); - self.read_exact(&mut buffer).await?; - buffer - } else { - // Read the message - let mut buffer = zenoh_buffers::vec::uninit(self.get_mtu() as usize); - let n = self.read(&mut buffer).await?; - buffer.truncate(n); - buffer - }; - - let mut reader = buffer.reader(); - let codec = Zenoh060::default(); - - let mut messages: Vec = Vec::with_capacity(1); - while reader.can_read() { - let msg: TransportMessage = codec - .read(&mut reader) - .map_err(|_| zerror!("Read error on link: {}", self))?; - messages.push(msg); - } - - Ok(messages) - } -} - -impl Deref for LinkUnicast { - type Target = Arc; - - #[inline] - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl Eq for LinkUnicast {} - -impl PartialEq for LinkUnicast { - fn eq(&self, other: &Self) -> bool { - self.get_src() == other.get_src() && self.get_dst() == other.get_dst() - } -} - -impl Hash for LinkUnicast { - fn hash(&self, state: &mut H) { - self.get_src().hash(state); - self.get_dst().hash(state); - } -} - -impl fmt::Display for LinkUnicast { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{} => {}", self.get_src(), self.get_dst()) - } -} - -impl fmt::Debug for LinkUnicast { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Link") - .field("src", &self.get_src()) - .field("dst", &self.get_dst()) - .field("mtu", &self.get_mtu()) - .field("is_reliable", &self.is_reliable()) - .field("is_streamed", &self.is_streamed()) - .finish() - } -} - -impl From> for LinkUnicast { - fn from(link: Arc) -> LinkUnicast { - LinkUnicast(link) - } -} - -/*************************************/ -/* MULTICAST */ -/*************************************/ -#[async_trait] -pub trait LinkManagerMulticastTrait: Send + Sync { - async fn new_link(&self, endpoint: &EndPoint) -> ZResult; -} - -pub type LinkManagerMulticast = Arc; - -#[derive(Clone)] -pub struct LinkMulticast(pub Arc); - -#[async_trait] -pub trait LinkMulticastTrait: Send + Sync { - fn get_mtu(&self) -> u16; - fn get_src(&self) -> &Locator; - fn get_dst(&self) -> &Locator; - fn is_reliable(&self) -> bool; - async fn write(&self, buffer: &[u8]) -> ZResult; - async fn write_all(&self, buffer: &[u8]) -> ZResult<()>; - async fn read<'a>(&'a self, buffer: &mut [u8]) -> ZResult<(usize, Cow<'a, Locator>)>; - async fn close(&self) -> ZResult<()>; -} - -impl LinkMulticast { - pub async fn write_transport_message(&self, msg: &TransportMessage) -> ZResult { - // Create the buffer for serializing the message - let mut buff = vec![]; - let mut writer = buff.writer(); - let codec = Zenoh060::default(); - codec - .write(&mut writer, msg) - .map_err(|_| zerror!("Encoding error on link: {}", self))?; - - // Send the message on the link - self.0.write_all(buff.as_slice()).await?; - - Ok(buff.len()) - } - - pub async fn read_transport_message(&self) -> ZResult<(Vec, Locator)> { - // Read the message - let mut buffer = zenoh_buffers::vec::uninit(self.get_mtu() as usize); - let (n, locator) = self.read(&mut buffer).await?; - buffer.truncate(n); - - let mut reader = buffer.reader(); - let codec = Zenoh060::default(); - - let mut messages: Vec = Vec::with_capacity(1); - while reader.can_read() { - let msg: TransportMessage = codec - .read(&mut reader) - .map_err(|_| zerror!("Invalid Message: Decoding error on link: {}", self))?; - messages.push(msg); - } - - Ok((messages, locator.into_owned())) - } -} - -impl Deref for LinkMulticast { - type Target = Arc; - #[inline] - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl Eq for LinkMulticast {} - -impl PartialEq for LinkMulticast { - fn eq(&self, other: &Self) -> bool { - self.get_src() == other.get_src() && self.get_dst() == other.get_dst() - } -} - -impl Hash for LinkMulticast { - fn hash(&self, state: &mut H) { - self.get_src().hash(state); - self.get_dst().hash(state); - } -} - -impl fmt::Display for LinkMulticast { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{} => {}", self.get_src(), self.get_dst()) - } -} - -impl fmt::Debug for LinkMulticast { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Link") - .field("src", &self.get_src()) - .field("dst", &self.get_dst()) - .field("mtu", &self.get_mtu()) - .field("is_reliable", &self.is_reliable()) - .finish() - } -} - -impl From> for LinkMulticast { - fn from(link: Arc) -> LinkMulticast { - LinkMulticast(link) - } -} diff --git a/io/zenoh-link-commons/src/multicast.rs b/io/zenoh-link-commons/src/multicast.rs new file mode 100644 index 0000000000..65bc7195b6 --- /dev/null +++ b/io/zenoh-link-commons/src/multicast.rs @@ -0,0 +1,136 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use alloc::{borrow::Cow, boxed::Box, sync::Arc, vec::Vec}; +use async_trait::async_trait; +use core::{ + fmt, + hash::{Hash, Hasher}, + ops::Deref, +}; +use zenoh_buffers::{reader::HasReader, writer::HasWriter}; +use zenoh_codec::{RCodec, WCodec, Zenoh080}; +use zenoh_protocol::{ + core::{EndPoint, Locator}, + transport::TransportMessage, +}; +use zenoh_result::{zerror, ZResult}; + +/*************************************/ +/* MANAGER */ +/*************************************/ +#[async_trait] +pub trait LinkManagerMulticastTrait: Send + Sync { + async fn new_link(&self, endpoint: &EndPoint) -> ZResult; +} + +pub type LinkManagerMulticast = Arc; + +/*************************************/ +/* LINK */ +/*************************************/ +#[derive(Clone)] +pub struct LinkMulticast(pub Arc); + +#[async_trait] +pub trait LinkMulticastTrait: Send + Sync { + fn get_mtu(&self) -> u16; + fn get_src(&self) -> &Locator; + fn get_dst(&self) -> &Locator; + fn is_reliable(&self) -> bool; + async fn write(&self, buffer: &[u8]) -> ZResult; + async fn write_all(&self, buffer: &[u8]) -> ZResult<()>; + async fn read<'a>(&'a self, buffer: &mut [u8]) -> ZResult<(usize, Cow<'a, Locator>)>; + async fn close(&self) -> ZResult<()>; +} + +impl LinkMulticast { + pub async fn send(&self, msg: &TransportMessage) -> ZResult { + // Create the buffer for serializing the message + let mut buff = Vec::new(); + + let codec = Zenoh080::new(); + let mut writer = buff.writer(); + + codec + .write(&mut writer, msg) + .map_err(|_| zerror!("Encoding error on link: {}", self))?; + + // Send the message on the link + self.0.write_all(buff.as_slice()).await?; + + Ok(buff.len()) + } + + pub async fn recv(&self) -> ZResult<(TransportMessage, Locator)> { + // Read the message + let mut buffer = zenoh_buffers::vec::uninit(self.get_mtu() as usize); + let (n, locator) = self.read(&mut buffer).await?; + buffer.truncate(n); + + let codec = Zenoh080::new(); + let mut reader = buffer.reader(); + + let msg: TransportMessage = codec + .read(&mut reader) + .map_err(|_| zerror!("Invalid Message: Decoding error on link: {}", self))?; + + Ok((msg, locator.into_owned())) + } +} + +impl Deref for LinkMulticast { + type Target = Arc; + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Eq for LinkMulticast {} + +impl PartialEq for LinkMulticast { + fn eq(&self, other: &Self) -> bool { + self.get_src() == other.get_src() && self.get_dst() == other.get_dst() + } +} + +impl Hash for LinkMulticast { + fn hash(&self, state: &mut H) { + self.get_src().hash(state); + self.get_dst().hash(state); + } +} + +impl fmt::Display for LinkMulticast { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{} => {}", self.get_src(), self.get_dst()) + } +} + +impl fmt::Debug for LinkMulticast { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Link") + .field("src", &self.get_src()) + .field("dst", &self.get_dst()) + .field("mtu", &self.get_mtu()) + .field("is_reliable", &self.is_reliable()) + .finish() + } +} + +impl From> for LinkMulticast { + fn from(link: Arc) -> LinkMulticast { + LinkMulticast(link) + } +} diff --git a/io/zenoh-link-commons/src/unicast.rs b/io/zenoh-link-commons/src/unicast.rs new file mode 100644 index 0000000000..7f3eb43518 --- /dev/null +++ b/io/zenoh-link-commons/src/unicast.rs @@ -0,0 +1,180 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use alloc::{boxed::Box, sync::Arc, vec::Vec}; +use async_trait::async_trait; +use core::{ + convert::TryFrom, + fmt, + hash::{Hash, Hasher}, + ops::Deref, +}; +use zenoh_buffers::{ + reader::HasReader, + writer::{HasWriter, Writer}, +}; +use zenoh_codec::{RCodec, WCodec, Zenoh080}; +use zenoh_protocol::{ + core::{EndPoint, Locator}, + transport::{BatchSize, TransportMessage}, +}; +use zenoh_result::{zerror, ZResult}; + +pub type LinkManagerUnicast = Arc; +#[async_trait] +pub trait LinkManagerUnicastTrait: Send + Sync { + async fn new_link(&self, endpoint: EndPoint) -> ZResult; + async fn new_listener(&self, endpoint: EndPoint) -> ZResult; + async fn del_listener(&self, endpoint: &EndPoint) -> ZResult<()>; + fn get_listeners(&self) -> Vec; + fn get_locators(&self) -> Vec; +} +pub type NewLinkChannelSender = flume::Sender; +pub trait ConstructibleLinkManagerUnicast: Sized { + fn new(new_link_sender: NewLinkChannelSender, config: T) -> ZResult; +} + +#[derive(Clone, PartialEq, Eq)] +pub enum LinkUnicastDirection { + Inbound, + Outbound, +} + +#[derive(Clone)] +pub struct LinkUnicast(pub Arc); + +#[async_trait] +pub trait LinkUnicastTrait: Send + Sync { + fn get_mtu(&self) -> u16; + fn get_src(&self) -> &Locator; + fn get_dst(&self) -> &Locator; + fn is_reliable(&self) -> bool; + fn is_streamed(&self) -> bool; + async fn write(&self, buffer: &[u8]) -> ZResult; + async fn write_all(&self, buffer: &[u8]) -> ZResult<()>; + async fn read(&self, buffer: &mut [u8]) -> ZResult; + async fn read_exact(&self, buffer: &mut [u8]) -> ZResult<()>; + async fn close(&self) -> ZResult<()>; +} + +impl LinkUnicast { + pub async fn send(&self, msg: &TransportMessage) -> ZResult { + const ERR: &str = "Write error on link: "; + + // Create the buffer for serializing the message + let mut buff = Vec::new(); + let mut writer = buff.writer(); + let codec = Zenoh080::new(); + + // Reserve 16 bits to write the length + if self.is_streamed() { + writer + .write_exact(BatchSize::MIN.to_le_bytes().as_slice()) + .map_err(|_| zerror!("{ERR}{self}"))?; + } + // Serialize the message + codec + .write(&mut writer, msg) + .map_err(|_| zerror!("{ERR}{self}"))?; + + // Write the length + if self.is_streamed() { + let num = BatchSize::MIN.to_le_bytes().len(); + let len = + BatchSize::try_from(writer.len() - num).map_err(|_| zerror!("{ERR}{self}"))?; + buff[..num].copy_from_slice(len.to_le_bytes().as_slice()); + } + + // Send the message on the link + self.0.write_all(buff.as_slice()).await?; + + Ok(buff.len()) + } + + pub async fn recv(&self) -> ZResult { + // Read from the link + let buffer = if self.is_streamed() { + // Read and decode the message length + let mut length_bytes = BatchSize::MIN.to_le_bytes(); + self.read_exact(&mut length_bytes).await?; + let to_read = BatchSize::from_le_bytes(length_bytes) as usize; + // Read the message + let mut buffer = zenoh_buffers::vec::uninit(to_read); + self.read_exact(&mut buffer).await?; + buffer + } else { + // Read the message + let mut buffer = zenoh_buffers::vec::uninit(self.get_mtu() as usize); + let n = self.read(&mut buffer).await?; + buffer.truncate(n); + buffer + }; + + let mut reader = buffer.reader(); + let codec = Zenoh080::new(); + + let msg: TransportMessage = codec + .read(&mut reader) + .map_err(|_| zerror!("Read error on link: {}", self))?; + + Ok(msg) + } +} + +impl Deref for LinkUnicast { + type Target = Arc; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Eq for LinkUnicast {} + +impl PartialEq for LinkUnicast { + fn eq(&self, other: &Self) -> bool { + self.get_src() == other.get_src() && self.get_dst() == other.get_dst() + } +} + +impl Hash for LinkUnicast { + fn hash(&self, state: &mut H) { + self.get_src().hash(state); + self.get_dst().hash(state); + } +} + +impl fmt::Display for LinkUnicast { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{} => {}", self.get_src(), self.get_dst()) + } +} + +impl fmt::Debug for LinkUnicast { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Link") + .field("src", &self.get_src()) + .field("dst", &self.get_dst()) + .field("mtu", &self.get_mtu()) + .field("is_reliable", &self.is_reliable()) + .field("is_streamed", &self.is_streamed()) + .finish() + } +} + +impl From> for LinkUnicast { + fn from(link: Arc) -> LinkUnicast { + LinkUnicast(link) + } +} diff --git a/io/zenoh-link/Cargo.toml b/io/zenoh-link/Cargo.toml index d228b02acb..79129740da 100644 --- a/io/zenoh-link/Cargo.toml +++ b/io/zenoh-link/Cargo.toml @@ -32,12 +32,12 @@ transport_udp = ["zenoh-link-udp"] transport_unixsock-stream = ["zenoh-link-unixsock_stream"] transport_ws = ["zenoh-link-ws"] transport_serial = ["zenoh-link-serial"] +transport_shm = ["zenoh-link-shm", "zenoh-link-shm/transport_shm"] [dependencies] async-std = { workspace = true } async-trait = { workspace = true } rcgen = { workspace = true, optional = true } -zenoh-cfg-properties = { workspace = true } zenoh-config = { workspace = true } zenoh-link-commons = { workspace = true } zenoh-link-quic = { workspace = true, optional = true } @@ -47,5 +47,6 @@ zenoh-link-tls = { workspace = true, optional = true } zenoh-link-udp = { workspace = true, optional = true } zenoh-link-unixsock_stream = { workspace = true, optional = true } zenoh-link-ws = { workspace = true, optional = true } +zenoh-link-shm = { workspace = true, optional = true } zenoh-protocol = { workspace = true } zenoh-result = { workspace = true } diff --git a/io/zenoh-link/src/lib.rs b/io/zenoh-link/src/lib.rs index 17115978fb..26efdd814c 100644 --- a/io/zenoh-link/src/lib.rs +++ b/io/zenoh-link/src/lib.rs @@ -18,10 +18,7 @@ //! //! [Click here for Zenoh's documentation](../zenoh/index.html) use std::collections::HashMap; -#[allow(unused_imports)] use std::sync::Arc; - -use zenoh_cfg_properties::Properties; use zenoh_config::Config; use zenoh_result::{bail, ZResult}; @@ -69,6 +66,13 @@ pub use zenoh_link_serial as serial; #[cfg(feature = "transport_serial")] use zenoh_link_serial::{LinkManagerUnicastSerial, SerialLocatorInspector, SERIAL_LOCATOR_PREFIX}; +#[cfg(feature = "transport_shm")] +pub use zenoh_link_shm as shm; +#[cfg(feature = "transport_shm")] +use zenoh_link_shm::{ + LinkManagerUnicastPipe, ShmConfigurator, ShmLocatorInspector, SHM_LOCATOR_PREFIX, +}; + pub use zenoh_link_commons::*; pub use zenoh_protocol::core::{EndPoint, Locator}; @@ -87,6 +91,8 @@ pub const PROTOCOLS: &[&str] = &[ unixsock_stream::UNIXSOCKSTREAM_LOCATOR_PREFIX, #[cfg(feature = "transport_serial")] serial::SERIAL_LOCATOR_PREFIX, + #[cfg(feature = "transport_shm")] + shm::SHM_LOCATOR_PREFIX, ]; #[derive(Default, Clone)] @@ -105,6 +111,8 @@ pub struct LocatorInspector { unixsock_stream_inspector: UnixSockStreamLocatorInspector, #[cfg(feature = "transport_serial")] serial_inspector: SerialLocatorInspector, + #[cfg(feature = "transport_shm")] + shm_inspector: ShmLocatorInspector, } impl LocatorInspector { pub async fn is_multicast(&self, locator: &Locator) -> ZResult { @@ -128,6 +136,8 @@ impl LocatorInspector { WS_LOCATOR_PREFIX => self.ws_inspector.is_multicast(locator).await, #[cfg(feature = "transport_serial")] SERIAL_LOCATOR_PREFIX => self.serial_inspector.is_multicast(locator).await, + #[cfg(feature = "transport_shm")] + SHM_LOCATOR_PREFIX => self.shm_inspector.is_multicast(locator).await, _ => bail!("Unsupported protocol: {}.", protocol), } } @@ -138,19 +148,22 @@ pub struct LinkConfigurator { quic_inspector: QuicConfigurator, #[cfg(feature = "transport_tls")] tls_inspector: TlsConfigurator, + #[cfg(feature = "transport_shm")] + shm_inspector: ShmConfigurator, } + impl LinkConfigurator { #[allow(unused_variables, unused_mut)] pub async fn configurations( &self, config: &Config, ) -> ( - HashMap, + HashMap, HashMap, ) { let mut configs = HashMap::new(); let mut errors = HashMap::new(); - let mut insert_config = |proto: String, cfg: ZResult| match cfg { + let mut insert_config = |proto: String, cfg: ZResult| match cfg { Ok(v) => { configs.insert(proto, v); } @@ -172,6 +185,13 @@ impl LinkConfigurator { self.tls_inspector.inspect_config(config).await, ); } + #[cfg(feature = "transport_shm")] + { + insert_config( + SHM_LOCATOR_PREFIX.into(), + self.shm_inspector.inspect_config(config).await, + ); + } (configs, errors) } } @@ -201,6 +221,8 @@ impl LinkManagerBuilderUnicast { WS_LOCATOR_PREFIX => Ok(Arc::new(LinkManagerUnicastWs::new(_manager))), #[cfg(feature = "transport_serial")] SERIAL_LOCATOR_PREFIX => Ok(Arc::new(LinkManagerUnicastSerial::new(_manager))), + #[cfg(feature = "transport_shm")] + SHM_LOCATOR_PREFIX => Ok(Arc::new(LinkManagerUnicastPipe::new(_manager))), _ => bail!("Unicast not supported for {} protocol", protocol), } } @@ -221,5 +243,3 @@ impl LinkManagerBuilderMulticast { } } } - -pub const WBUF_SIZE: usize = 64; diff --git a/io/zenoh-links/zenoh-link-quic/Cargo.toml b/io/zenoh-links/zenoh-link-quic/Cargo.toml index 65d2980b2c..4d9a4e0b3d 100644 --- a/io/zenoh-links/zenoh-link-quic/Cargo.toml +++ b/io/zenoh-links/zenoh-link-quic/Cargo.toml @@ -34,7 +34,7 @@ quinn = { workspace = true } rustls = { workspace = true } rustls-native-certs = { workspace = true } rustls-pemfile = { workspace = true } -zenoh-cfg-properties = { workspace = true } +rustls-webpki = { workspace = true } zenoh-config = { workspace = true } zenoh-core = { workspace = true } zenoh-link-commons = { workspace = true } diff --git a/io/zenoh-links/zenoh-link-quic/src/lib.rs b/io/zenoh-links/zenoh-link-quic/src/lib.rs index c0fda8fed5..f5449a767e 100644 --- a/io/zenoh-links/zenoh-link-quic/src/lib.rs +++ b/io/zenoh-links/zenoh-link-quic/src/lib.rs @@ -24,11 +24,13 @@ use config::{ TLS_SERVER_PRIVATE_KEY_FILE, }; use std::net::SocketAddr; -use zenoh_cfg_properties::Properties; -use zenoh_config::{Config, Locator, ZN_FALSE, ZN_TRUE}; +use zenoh_config::Config; use zenoh_core::zconfigurable; use zenoh_link_commons::{ConfigurationInspector, LocatorInspector}; -use zenoh_protocol::core::endpoint::Address; +use zenoh_protocol::core::{ + endpoint::{Address, Parameters}, + Locator, +}; use zenoh_result::{bail, ZResult}; mod unicast; @@ -49,49 +51,47 @@ pub const QUIC_LOCATOR_PREFIX: &str = "quic"; #[derive(Default, Clone, Copy, Debug)] pub struct QuicLocatorInspector; + #[async_trait] impl LocatorInspector for QuicLocatorInspector { fn protocol(&self) -> &str { QUIC_LOCATOR_PREFIX } + async fn is_multicast(&self, _locator: &Locator) -> ZResult { Ok(false) } } + #[derive(Default, Clone, Copy, Debug)] pub struct QuicConfigurator; + #[async_trait] impl ConfigurationInspector for QuicConfigurator { - async fn inspect_config(&self, config: &Config) -> ZResult { - let mut properties = Properties::default(); + async fn inspect_config(&self, config: &Config) -> ZResult { + let mut ps: Vec<(&str, &str)> = vec![]; let c = config.transport().link().tls(); if let Some(tls_ca_certificate) = c.root_ca_certificate() { - properties.insert( - TLS_ROOT_CA_CERTIFICATE_FILE.into(), - tls_ca_certificate.into(), - ); + ps.push((TLS_ROOT_CA_CERTIFICATE_FILE, tls_ca_certificate)); } if let Some(tls_server_private_key) = c.server_private_key() { - properties.insert( - TLS_SERVER_PRIVATE_KEY_FILE.into(), - tls_server_private_key.into(), - ); + ps.push((TLS_SERVER_PRIVATE_KEY_FILE, tls_server_private_key)); } if let Some(tls_server_certificate) = c.server_certificate() { - properties.insert( - TLS_SERVER_CERTIFICATE_FILE.into(), - tls_server_certificate.into(), - ); + ps.push((TLS_SERVER_CERTIFICATE_FILE, tls_server_certificate)); } if let Some(server_name_verification) = c.server_name_verification() { match server_name_verification { - true => properties.insert(TLS_SERVER_NAME_VERIFICATION.into(), ZN_TRUE.into()), - false => properties.insert(TLS_SERVER_NAME_VERIFICATION.into(), ZN_FALSE.into()), + true => ps.push((TLS_SERVER_NAME_VERIFICATION, "true")), + false => ps.push((TLS_SERVER_NAME_VERIFICATION, "false")), }; } - Ok(properties) + let mut s = String::new(); + Parameters::extend(ps.drain(..), &mut s); + + Ok(s) } } @@ -109,19 +109,17 @@ zconfigurable! { } pub mod config { - use zenoh_cfg_properties::config::*; - - pub const TLS_ROOT_CA_CERTIFICATE_FILE: &str = ZN_TLS_ROOT_CA_CERTIFICATE_STR; - pub const TLS_ROOT_CA_CERTIFICATE_RAW: &str = "tls_root_ca_certificate_raw"; + pub const TLS_ROOT_CA_CERTIFICATE_FILE: &str = "root_ca_certificate_file"; + pub const TLS_ROOT_CA_CERTIFICATE_RAW: &str = "root_ca_certificate_raw"; - pub const TLS_SERVER_PRIVATE_KEY_FILE: &str = ZN_TLS_SERVER_PRIVATE_KEY_STR; - pub const TLS_SERVER_PRIVATE_KEY_RAW: &str = "tls_server_private_key_raw"; + pub const TLS_SERVER_PRIVATE_KEY_FILE: &str = "server_private_key_file"; + pub const TLS_SERVER_PRIVATE_KEY_RAW: &str = "server_private_key_raw"; - pub const TLS_SERVER_CERTIFICATE_FILE: &str = ZN_TLS_SERVER_CERTIFICATE_STR; + pub const TLS_SERVER_CERTIFICATE_FILE: &str = "tls_server_certificate_file"; pub const TLS_SERVER_CERTIFICATE_RAW: &str = "tls_server_certificate_raw"; - pub const TLS_SERVER_NAME_VERIFICATION: &str = ZN_TLS_SERVER_NAME_VERIFICATION_STR; - pub const TLS_SERVER_NAME_VERIFICATION_DEFAULT: &str = ZN_TLS_SERVER_NAME_VERIFICATION_DEFAULT; + pub const TLS_SERVER_NAME_VERIFICATION: &str = "server_name_verification"; + pub const TLS_SERVER_NAME_VERIFICATION_DEFAULT: &str = "true"; } async fn get_quic_addr(address: &Address<'_>) -> ZResult { diff --git a/io/zenoh-links/zenoh-link-serial/Cargo.toml b/io/zenoh-links/zenoh-link-serial/Cargo.toml index c652d126a9..aedf3eb849 100644 --- a/io/zenoh-links/zenoh-link-serial/Cargo.toml +++ b/io/zenoh-links/zenoh-link-serial/Cargo.toml @@ -39,7 +39,6 @@ log = { workspace = true } tokio = { workspace = true, features = ["io-std", "macros", "net", "rt-multi-thread", "time", "io-util"] } uuid = { workspace = true, default-features = true } z-serial = { workspace = true } -zenoh-cfg-properties = { workspace = true } zenoh-collections = { workspace = true } zenoh-config = { workspace = true } zenoh-core = { workspace = true } diff --git a/io/zenoh-links/zenoh-link-serial/src/lib.rs b/io/zenoh-links/zenoh-link-serial/src/lib.rs index 6ceb4abf86..fb4d7fcc12 100644 --- a/io/zenoh-links/zenoh-link-serial/src/lib.rs +++ b/io/zenoh-links/zenoh-link-serial/src/lib.rs @@ -53,6 +53,7 @@ impl LocatorInspector for SerialLocatorInspector { fn protocol(&self) -> &str { SERIAL_LOCATOR_PREFIX } + async fn is_multicast(&self, _locator: &Locator) -> ZResult { Ok(false) } diff --git a/commons/zenoh-cfg-properties/Cargo.toml b/io/zenoh-links/zenoh-link-shm/Cargo.toml similarity index 56% rename from commons/zenoh-cfg-properties/Cargo.toml rename to io/zenoh-links/zenoh-link-shm/Cargo.toml index 18c5bd48b3..f29be5ade8 100644 --- a/commons/zenoh-cfg-properties/Cargo.toml +++ b/io/zenoh-links/zenoh-link-shm/Cargo.toml @@ -10,22 +10,40 @@ # # Contributors: # ZettaScale Zenoh Team, +# [package] rust-version = { workspace = true } -name = "zenoh-cfg-properties" +name = "zenoh-link-shm" version = { workspace = true } repository = { workspace = true } homepage = { workspace = true } -authors = [ - "kydos ", - "Luca Cominardi ", - "Pierre Avital ", -] +authors = { workspace = true } edition = { workspace = true } license = { workspace = true } categories = { workspace = true } description = "Internal crate for zenoh." # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +transport_shm = [] + [dependencies] -zenoh-result = { workspace = true, features = ["default"] } +async-std = { workspace = true } +async-trait = { workspace = true } +async-io = ">= 1.13.0" +log = { workspace = true } +rand = { workspace = true } +zenoh-buffers = { workspace = true } +zenoh-core = { workspace = true } +zenoh-config = { workspace = true } +zenoh-link-commons = { workspace = true } +zenoh-protocol = { workspace = true } +zenoh-result = { workspace = true } + +[target.'cfg(unix)'.dependencies] +unix-named-pipe = ">= 0.2.0" +nix = { workspace = true } +filepath = ">=0.1.2" + +[target.'cfg(all(not(target_os="macos"), unix))'.dependencies] +advisory-lock = ">= 0.3.0" diff --git a/io/zenoh-links/zenoh-link-shm/src/lib.rs b/io/zenoh-links/zenoh-link-shm/src/lib.rs new file mode 100644 index 0000000000..96e373a66d --- /dev/null +++ b/io/zenoh-links/zenoh-link-shm/src/lib.rs @@ -0,0 +1,24 @@ +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// + +//! ⚠️ WARNING ⚠️ +//! +//! This crate is intended for Zenoh's internal use. +//! +//! [Click here for Zenoh's documentation](../zenoh/index.html) + +#[cfg(unix)] +mod unix; +#[cfg(unix)] +pub use unix::*; diff --git a/io/zenoh-links/zenoh-link-shm/src/unix/mod.rs b/io/zenoh-links/zenoh-link-shm/src/unix/mod.rs new file mode 100644 index 0000000000..81a02633c1 --- /dev/null +++ b/io/zenoh-links/zenoh-link-shm/src/unix/mod.rs @@ -0,0 +1,74 @@ +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// + +//! ⚠️ WARNING ⚠️ +//! +//! This crate is intended for Zenoh's internal use. +//! +//! [Click here for Zenoh's documentation](../zenoh/index.html) +pub mod unicast; + +use async_trait::async_trait; +pub use unicast::*; +use zenoh_config::Config; +use zenoh_core::zconfigurable; +use zenoh_link_commons::{ConfigurationInspector, LocatorInspector}; +use zenoh_protocol::core::{Locator, Parameters}; +use zenoh_result::ZResult; + +pub const SHM_LOCATOR_PREFIX: &str = "shm"; + +#[derive(Default, Clone, Copy)] +pub struct ShmLocatorInspector; +#[async_trait] +impl LocatorInspector for ShmLocatorInspector { + fn protocol(&self) -> &str { + SHM_LOCATOR_PREFIX + } + + async fn is_multicast(&self, _locator: &Locator) -> ZResult { + Ok(false) + } +} + +#[derive(Default, Clone, Copy, Debug)] +pub struct ShmConfigurator; +#[async_trait] +impl ConfigurationInspector for ShmConfigurator { + async fn inspect_config(&self, config: &Config) -> ZResult { + let mut properties: Vec<(&str, &str)> = vec![]; + + let c = config.transport().link().shared_memory(); + let shm_access_mask_; + if let Some(shm_access_mask) = c.shm_access_mask() { + shm_access_mask_ = shm_access_mask.to_string(); + properties.push((config::SHM_ACCESS_MASK, &shm_access_mask_)); + } + + let mut s = String::new(); + Parameters::extend(properties.drain(..), &mut s); + + Ok(s) + } +} + +zconfigurable! { + // Default access mask for SHM resources + static ref SHM_ACCESS_MASK: u32 = config::SHM_ACCESS_MASK_DEFAULT; +} + +pub mod config { + pub const SHM_ACCESS_MASK: &str = "shm_mask"; + pub const SHM_ACCESS_MASK_DEFAULT: u32 = 0o777; +} diff --git a/io/zenoh-links/zenoh-link-shm/src/unix/unicast.rs b/io/zenoh-links/zenoh-link-shm/src/unix/unicast.rs new file mode 100644 index 0000000000..27a9f0e037 --- /dev/null +++ b/io/zenoh-links/zenoh-link-shm/src/unix/unicast.rs @@ -0,0 +1,586 @@ +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::config; +#[cfg(not(target_os = "macos"))] +use advisory_lock::{AdvisoryFileLock, FileLockMode}; +use async_io::Async; +use async_std::fs::remove_file; +use async_std::task::JoinHandle; +use async_trait::async_trait; +use filepath::FilePath; +use nix::unistd::unlink; +use rand::Rng; +use std::cell::UnsafeCell; +use std::collections::HashMap; +use std::fmt; +use std::fs::File; +use std::io::{Read, Write}; +use std::sync::Arc; +use zenoh_core::{zasyncread, zasyncwrite}; +use zenoh_protocol::core::{EndPoint, Locator}; + +use unix_named_pipe::{create, open_read, open_write}; + +use zenoh_link_commons::{ + ConstructibleLinkManagerUnicast, LinkManagerUnicastTrait, LinkUnicast, LinkUnicastTrait, + NewLinkChannelSender, +}; +use zenoh_result::{bail, ZResult}; + +use super::SHM_ACCESS_MASK; + +const LINUX_PIPE_MAX_MTU: u16 = 65_535; +const LINUX_PIPE_DEDICATE_TRIES: usize = 100; + +static PIPE_INVITATION: &[u8] = &[0xDE, 0xAD, 0xBE, 0xEF]; + +struct Invitation; +impl Invitation { + async fn send(suffix: u32, pipe: &mut PipeW) -> ZResult<()> { + let msg: [u8; 8] = { + let mut msg: [u8; 8] = [0; 8]; + let (one, two) = msg.split_at_mut(PIPE_INVITATION.len()); + one.copy_from_slice(PIPE_INVITATION); + two.copy_from_slice(&suffix.to_ne_bytes()); + msg + }; + pipe.write_all(&msg).await + } + + async fn receive(pipe: &mut PipeR) -> ZResult { + let mut msg: [u8; 8] = [0; 8]; + pipe.read_exact(&mut msg).await?; + if !msg.starts_with(PIPE_INVITATION) { + bail!("Unexpected invitation received during pipe handshake!") + } + + let suffix_bytes: &[u8; 4] = &msg[4..].try_into()?; + let suffix = u32::from_ne_bytes(*suffix_bytes); + Ok(suffix) + } + + async fn confirm(suffix: u32, pipe: &mut PipeW) -> ZResult<()> { + Self::send(suffix, pipe).await + } + + async fn expect(expected_suffix: u32, pipe: &mut PipeR) -> ZResult<()> { + let recived_suffix = Self::receive(pipe).await?; + if recived_suffix != expected_suffix { + bail!( + "Suffix mismatch: expected {} got {}", + expected_suffix, + recived_suffix + ) + } + Ok(()) + } +} + +struct PipeR { + pipe: Async, +} + +impl Drop for PipeR { + fn drop(&mut self) { + if let Ok(path) = self.pipe.as_mut().path() { + let _ = unlink(&path); + } + } +} +impl PipeR { + async fn new(path: &str, access_mode: u32) -> ZResult { + // create, open and lock named pipe + let pipe_file = Self::create_and_open_unique_pipe_for_read(path, access_mode).await?; + // create async_io wrapper for pipe's file descriptor + let pipe = Async::new(pipe_file)?; + Ok(Self { pipe }) + } + + async fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> ZResult { + let result = self + .pipe + .read_with_mut(|pipe| match pipe.read(&mut buf[..]) { + Ok(0) => Err(async_std::io::ErrorKind::WouldBlock.into()), + Ok(val) => Ok(val), + Err(e) => Err(e), + }) + .await?; + ZResult::Ok(result) + } + + async fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> ZResult<()> { + let mut r: usize = 0; + self.pipe + .read_with_mut(|pipe| match pipe.read(&mut buf[r..]) { + Ok(0) => Err(async_std::io::ErrorKind::WouldBlock.into()), + Ok(val) => { + r += val; + if r == buf.len() { + return Ok(()); + } + Err(async_std::io::ErrorKind::WouldBlock.into()) + } + Err(e) => Err(e), + }) + .await?; + ZResult::Ok(()) + } + + async fn create_and_open_unique_pipe_for_read(path_r: &str, access_mode: u32) -> ZResult { + let r_was_created = create(path_r, Some(access_mode)); + let open_result = Self::open_unique_pipe_for_read(path_r); + match (open_result.as_ref(), r_was_created) { + (Err(_), Ok(_)) => { + // clean-up in case of failure + let _ = remove_file(path_r).await; + } + (Ok(mut pipe_file), Err(_)) => { + // drop all the data from the pipe in case if it already exists + let mut buf: [u8; 1] = [0; 1]; + while let Ok(val) = pipe_file.read(&mut buf) { + if val == 0 { + break; + } + } + } + _ => {} + } + + open_result + } + + fn open_unique_pipe_for_read(path: &str) -> ZResult { + let read = open_read(path)?; + #[cfg(not(target_os = "macos"))] + read.try_lock(FileLockMode::Exclusive)?; + Ok(read) + } +} + +struct PipeW { + pipe: Async, +} +impl PipeW { + async fn new(path: &str) -> ZResult { + // create, open and lock named pipe + let pipe_file = Self::open_unique_pipe_for_write(path)?; + // create async_io wrapper for pipe's file descriptor + let pipe = Async::new(pipe_file)?; + Ok(Self { pipe }) + } + + async fn write<'a>(&'a mut self, buf: &'a [u8]) -> ZResult { + let result = self + .pipe + .write_with_mut(|pipe| match pipe.write(buf) { + Ok(0) => Err(async_std::io::ErrorKind::WouldBlock.into()), + Ok(val) => Ok(val), + Err(e) => Err(e), + }) + .await?; + ZResult::Ok(result) + } + + async fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> ZResult<()> { + let mut r: usize = 0; + self.pipe + .write_with_mut(|pipe| match pipe.write(&buf[r..]) { + Ok(0) => Err(async_std::io::ErrorKind::WouldBlock.into()), + Ok(val) => { + r += val; + if r == buf.len() { + return Ok(()); + } + Err(async_std::io::ErrorKind::WouldBlock.into()) + } + Err(e) => Err(e), + }) + .await?; + ZResult::Ok(()) + } + + fn open_unique_pipe_for_write(path: &str) -> ZResult { + let write = open_write(path)?; + // the file must be already locked at the other side... + #[cfg(not(target_os = "macos"))] + if write.try_lock(FileLockMode::Exclusive).is_ok() { + let _ = write.unlock(); + bail!("no listener...") + } + Ok(write) + } +} + +async fn handle_incoming_connections( + endpoint: &EndPoint, + manager: &Arc, + request_channel: &mut PipeR, + path_downlink: &str, + path_uplink: &str, + access_mode: u32, +) -> ZResult<()> { + // read invitation from the request channel + let suffix = Invitation::receive(request_channel).await?; + + // gererate uplink and downlink names + let (dedicated_downlink_path, dedicated_uplink_path) = + get_dedicated_pipe_names(path_downlink, path_uplink, suffix); + + // create dedicated downlink and uplink + let mut dedicated_downlink = PipeW::new(&dedicated_downlink_path).await?; + let mut dedicated_uplink = PipeR::new(&dedicated_uplink_path, access_mode).await?; + + // confirm over the dedicated chanel + Invitation::confirm(suffix, &mut dedicated_downlink).await?; + + // got confirmation over the dedicated chanel + Invitation::expect(suffix, &mut dedicated_uplink).await?; + + // create Locators + let local = Locator::new( + endpoint.protocol(), + dedicated_uplink_path, + endpoint.metadata(), + )?; + let remote = Locator::new( + endpoint.protocol(), + dedicated_downlink_path, + endpoint.metadata(), + )?; + + // send newly established link to manager + manager + .send_async(LinkUnicast(Arc::new(UnicastPipe { + r: UnsafeCell::new(dedicated_uplink), + w: UnsafeCell::new(dedicated_downlink), + local, + remote, + }))) + .await?; + + ZResult::Ok(()) +} + +struct UnicastPipeListener { + listening_task_handle: JoinHandle>, + uplink_locator: Locator, +} +impl UnicastPipeListener { + async fn listen(endpoint: EndPoint, manager: Arc) -> ZResult { + let (path_uplink, path_downlink, access_mode) = parse_pipe_endpoint(&endpoint); + let local = Locator::new( + endpoint.protocol(), + path_uplink.as_str(), + endpoint.metadata(), + )?; + + // create request channel + let mut request_channel = PipeR::new(&path_uplink, access_mode).await?; + + // create listening task + let listening_task_handle = async_std::task::spawn(async move { + loop { + let _ = handle_incoming_connections( + &endpoint, + &manager, + &mut request_channel, + &path_downlink, + &path_uplink, + access_mode, + ) + .await; + } + }); + + Ok(Self { + listening_task_handle, + uplink_locator: local, + }) + } + + async fn stop_listening(self) { + self.listening_task_handle.cancel().await; + } +} + +fn get_dedicated_pipe_names( + path_downlink: &str, + path_uplink: &str, + suffix: u32, +) -> (String, String) { + let suffix_str = suffix.to_string(); + let path_uplink = path_uplink.to_string() + &suffix_str; + let path_downlink = path_downlink.to_string() + &suffix_str; + (path_downlink, path_uplink) +} + +async fn create_pipe( + path_uplink: &str, + path_downlink: &str, + access_mode: u32, +) -> ZResult<(PipeR, u32, String, String)> { + // generate random suffix + let suffix: u32 = rand::thread_rng().gen(); + + // gererate uplink and downlink names + let (path_downlink, path_uplink) = get_dedicated_pipe_names(path_downlink, path_uplink, suffix); + + // try create uplink and downlink pipes to ensure that the selected suffix is available + let downlink = PipeR::new(&path_downlink, access_mode).await?; + let _uplink = PipeR::new(&path_uplink, access_mode).await?; // uplink would be dropped, that is OK! + + Ok((downlink, suffix, path_downlink, path_uplink)) +} + +async fn dedicate_pipe( + path_uplink: &str, + path_downlink: &str, + access_mode: u32, +) -> ZResult<(PipeR, u32, String, String)> { + for _ in 0..LINUX_PIPE_DEDICATE_TRIES { + match create_pipe(path_uplink, path_downlink, access_mode).await { + Err(_) => {} + val => { + return val; + } + } + } + bail!("Unabe to dedicate pipe!") +} + +struct UnicastPipeClient; +impl UnicastPipeClient { + async fn connect_to(endpoint: EndPoint) -> ZResult { + let (path_uplink, path_downlink, access_mode) = parse_pipe_endpoint(&endpoint); + + // open the request channel + // this channel would be used to invite listener to the dedicated channel + // listener owns the request channel, so failure of this call means that there is nobody listening on the provided endpoint + let mut request_channel = PipeW::new(&path_uplink).await?; + + // create dedicated channel prerequisities. The creation code also ensures that nobody else would use the same channel concurrently + let ( + mut dedicated_downlink, + dedicated_suffix, + dedicated_donlink_path, + dedicated_uplink_path, + ) = dedicate_pipe(&path_uplink, &path_downlink, access_mode).await?; + + // invite the listener to our dedicated channel over the requet channel + Invitation::send(dedicated_suffix, &mut request_channel).await?; + + // read responce that should be sent over the dedicated channel, confirming that everything is OK + // on the listener's side and it is already working with the dedicated channel + Invitation::expect(dedicated_suffix, &mut dedicated_downlink).await?; + + // open dedicated uplink + let mut dedicated_uplink = PipeW::new(&dedicated_uplink_path).await?; + + // final confirmation over the dedicated uplink + Invitation::confirm(dedicated_suffix, &mut dedicated_uplink).await?; + + // create Locators + let local = Locator::new( + endpoint.protocol(), + dedicated_donlink_path, + endpoint.metadata(), + )?; + let remote = Locator::new( + endpoint.protocol(), + dedicated_uplink_path, + endpoint.metadata(), + )?; + + Ok(UnicastPipe { + r: UnsafeCell::new(dedicated_downlink), + w: UnsafeCell::new(dedicated_uplink), + local, + remote, + }) + } +} + +struct UnicastPipe { + // The underlying pipes wrapped into async_io + // SAFETY: Async requires &mut for read and write operations. This means + // that concurrent reads and writes are not possible. To achieve that, + // we use an UnsafeCell for interior mutability. Using an UnsafeCell + // is safe in our case since the transmission and reception logic + // already ensures that no concurrent reads or writes can happen on + // the same stream: there is only one task at the time that writes on + // the stream and only one task at the time that reads from the stream. + r: UnsafeCell, + w: UnsafeCell, + local: Locator, + remote: Locator, +} + +impl UnicastPipe { + // SAFETY: It is safe to suppress Clippy warning since no concurrent access will ever happen. + // The write and read pipes are independent and support full-duplex operation, + // and single-direction operations are aligned at the transport side and will never access link concurrently + #[allow(clippy::mut_from_ref)] + fn get_r_mut(&self) -> &mut PipeR { + unsafe { &mut *self.r.get() } + } + + #[allow(clippy::mut_from_ref)] + fn get_w_mut(&self) -> &mut PipeW { + unsafe { &mut *self.w.get() } + } +} +// Promise that proper synchronization exists *around accesses*. +unsafe impl Sync for UnicastPipe {} + +impl Drop for UnicastPipe { + fn drop(&mut self) {} +} + +#[async_trait] +impl LinkUnicastTrait for UnicastPipe { + async fn close(&self) -> ZResult<()> { + log::trace!("Closing SHM Pipe link: {}", self); + Ok(()) + } + + async fn write(&self, buffer: &[u8]) -> ZResult { + self.get_w_mut().write(buffer).await + } + + async fn write_all(&self, buffer: &[u8]) -> ZResult<()> { + self.get_w_mut().write_all(buffer).await + } + + async fn read(&self, buffer: &mut [u8]) -> ZResult { + self.get_r_mut().read(buffer).await + } + + async fn read_exact(&self, buffer: &mut [u8]) -> ZResult<()> { + self.get_r_mut().read_exact(buffer).await + } + + #[inline(always)] + fn get_src(&self) -> &Locator { + &self.local + } + + #[inline(always)] + fn get_dst(&self) -> &Locator { + &self.remote + } + + #[inline(always)] + fn get_mtu(&self) -> u16 { + LINUX_PIPE_MAX_MTU + } + + #[inline(always)] + fn is_reliable(&self) -> bool { + true + } + + #[inline(always)] + fn is_streamed(&self) -> bool { + true + } +} + +impl fmt::Display for UnicastPipe { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{} => {}", self.local, self.remote)?; + Ok(()) + } +} + +impl fmt::Debug for UnicastPipe { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Shm") + .field("src", &self.local) + .field("dst", &self.remote) + .finish() + } +} + +pub struct LinkManagerUnicastPipe { + manager: Arc, + listeners: async_std::sync::RwLock>, +} + +impl LinkManagerUnicastPipe { + pub fn new(manager: NewLinkChannelSender) -> Self { + Self { + manager: Arc::new(manager), + listeners: async_std::sync::RwLock::new(HashMap::new()), + } + } +} +impl ConstructibleLinkManagerUnicast<()> for LinkManagerUnicastPipe { + fn new(new_link_sender: NewLinkChannelSender, _: ()) -> ZResult { + Ok(Self::new(new_link_sender)) + } +} + +#[async_trait] +impl LinkManagerUnicastTrait for LinkManagerUnicastPipe { + async fn new_link(&self, endpoint: EndPoint) -> ZResult { + let pipe = UnicastPipeClient::connect_to(endpoint).await?; + Ok(LinkUnicast(Arc::new(pipe))) + } + + async fn new_listener(&self, endpoint: EndPoint) -> ZResult { + let listener = UnicastPipeListener::listen(endpoint.clone(), self.manager.clone()).await?; + let locator = listener.uplink_locator.clone(); + zasyncwrite!(self.listeners).insert(endpoint, listener); + Ok(locator) + } + + async fn del_listener(&self, endpoint: &EndPoint) -> ZResult<()> { + let removed = zasyncwrite!(self.listeners).remove(endpoint); + match removed { + Some(val) => { + val.stop_listening().await; + Ok(()) + } + None => bail!("No listener found for endpoint {}", endpoint), + } + } + + fn get_listeners(&self) -> Vec { + async_std::task::block_on(async { zasyncread!(self.listeners) }) + .keys() + .cloned() + .collect() + } + + fn get_locators(&self) -> Vec { + async_std::task::block_on(async { zasyncread!(self.listeners) }) + .values() + .map(|v| v.uplink_locator.clone()) + .collect() + } +} + +fn parse_pipe_endpoint(endpoint: &EndPoint) -> (String, String, u32) { + let address = endpoint.address(); + let path = address.as_str(); + let path_uplink = path.to_string() + "_uplink"; + let path_downlink = path.to_string() + "_downlink"; + let access_mode = endpoint + .config() + .get(config::SHM_ACCESS_MASK) + .map_or(*SHM_ACCESS_MASK, |val| { + val.parse().unwrap_or(*SHM_ACCESS_MASK) + }); + (path_uplink, path_downlink, access_mode) +} diff --git a/io/zenoh-links/zenoh-link-tls/Cargo.toml b/io/zenoh-links/zenoh-link-tls/Cargo.toml index 96491b3e2b..dfe2cd3562 100644 --- a/io/zenoh-links/zenoh-link-tls/Cargo.toml +++ b/io/zenoh-links/zenoh-link-tls/Cargo.toml @@ -34,7 +34,6 @@ log = { workspace = true } rustls-pemfile = { workspace = true } rustls-webpki = { workspace = true } webpki-roots = { workspace = true } -zenoh-cfg-properties = { workspace = true } zenoh-config = { workspace = true } zenoh-core = { workspace = true } zenoh-link-commons = { workspace = true } diff --git a/io/zenoh-links/zenoh-link-tls/src/lib.rs b/io/zenoh-links/zenoh-link-tls/src/lib.rs index c04e5a0e68..8fb2f899a8 100644 --- a/io/zenoh-links/zenoh-link-tls/src/lib.rs +++ b/io/zenoh-links/zenoh-link-tls/src/lib.rs @@ -17,8 +17,7 @@ //! This crate is intended for Zenoh's internal use. //! //! [Click here for Zenoh's documentation](../zenoh/index.html) -use std::{convert::TryFrom, net::SocketAddr}; - +use async_rustls::rustls::ServerName; use async_std::net::ToSocketAddrs; use async_trait::async_trait; use config::{ @@ -26,11 +25,14 @@ use config::{ TLS_ROOT_CA_CERTIFICATE_FILE, TLS_SERVER_CERTIFICATE_FILE, TLS_SERVER_NAME_VERIFICATION, TLS_SERVER_PRIVATE_KEY_FILE, }; -use zenoh_cfg_properties::Properties; -use zenoh_config::{Config, ZN_FALSE, ZN_TRUE}; +use std::{convert::TryFrom, net::SocketAddr}; +use zenoh_config::Config; use zenoh_core::zconfigurable; use zenoh_link_commons::{ConfigurationInspector, LocatorInspector}; -use zenoh_protocol::core::{endpoint::Address, Locator}; +use zenoh_protocol::core::{ + endpoint::{self, Address}, + Locator, +}; use zenoh_result::{bail, zerror, ZResult}; mod unicast; @@ -53,62 +55,52 @@ impl LocatorInspector for TlsLocatorInspector { fn protocol(&self) -> &str { TLS_LOCATOR_PREFIX } + async fn is_multicast(&self, _locator: &Locator) -> ZResult { Ok(false) } } #[derive(Default, Clone, Copy, Debug)] pub struct TlsConfigurator; + #[async_trait] impl ConfigurationInspector for TlsConfigurator { - async fn inspect_config(&self, config: &Config) -> ZResult { - let mut properties = Properties::default(); + async fn inspect_config(&self, config: &Config) -> ZResult { + let mut ps: Vec<(&str, &str)> = vec![]; let c = config.transport().link().tls(); - if let Some(tls_ca_certificate) = c.root_ca_certificate() { - properties.insert( - TLS_ROOT_CA_CERTIFICATE_FILE.into(), - tls_ca_certificate.into(), - ); + if let Some(ca_certificate) = c.root_ca_certificate() { + ps.push((TLS_ROOT_CA_CERTIFICATE_FILE, ca_certificate)); } - if let Some(tls_server_private_key) = c.server_private_key() { - properties.insert( - TLS_SERVER_PRIVATE_KEY_FILE.into(), - tls_server_private_key.into(), - ); + if let Some(server_private_key) = c.server_private_key() { + ps.push((TLS_SERVER_PRIVATE_KEY_FILE, server_private_key)); } - if let Some(tls_server_certificate) = c.server_certificate() { - properties.insert( - TLS_SERVER_CERTIFICATE_FILE.into(), - tls_server_certificate.into(), - ); + if let Some(server_certificate) = c.server_certificate() { + ps.push((TLS_SERVER_CERTIFICATE_FILE, server_certificate)); } - if let Some(tls_client_auth) = c.client_auth() { - match tls_client_auth { - true => properties.insert(TLS_CLIENT_AUTH.into(), ZN_TRUE.into()), - false => properties.insert(TLS_CLIENT_AUTH.into(), ZN_FALSE.into()), + if let Some(client_auth) = c.client_auth() { + match client_auth { + true => ps.push((TLS_CLIENT_AUTH, "true")), + false => ps.push((TLS_CLIENT_AUTH, "false")), }; } - if let Some(tls_client_private_key) = c.client_private_key() { - properties.insert( - TLS_CLIENT_PRIVATE_KEY_FILE.into(), - tls_client_private_key.into(), - ); + if let Some(client_private_key) = c.client_private_key() { + ps.push((TLS_CLIENT_PRIVATE_KEY_FILE, client_private_key)); } - if let Some(tls_client_certificate) = c.client_certificate() { - properties.insert( - TLS_CLIENT_CERTIFICATE_FILE.into(), - tls_client_certificate.into(), - ); + if let Some(client_certificate) = c.client_certificate() { + ps.push((TLS_CLIENT_CERTIFICATE_FILE, client_certificate)); } if let Some(server_name_verification) = c.server_name_verification() { match server_name_verification { - true => properties.insert(TLS_SERVER_NAME_VERIFICATION.into(), ZN_TRUE.into()), - false => properties.insert(TLS_SERVER_NAME_VERIFICATION.into(), ZN_FALSE.into()), + true => ps.push((TLS_SERVER_NAME_VERIFICATION, "true")), + false => ps.push((TLS_SERVER_NAME_VERIFICATION, "false")), }; } - Ok(properties) + let mut s = String::new(); + endpoint::Parameters::extend(ps.drain(..), &mut s); + + Ok(s) } } @@ -126,27 +118,24 @@ zconfigurable! { } pub mod config { - use zenoh_cfg_properties::config::*; - pub const TLS_ROOT_CA_CERTIFICATE_FILE: &str = ZN_TLS_ROOT_CA_CERTIFICATE_STR; - pub const TLS_ROOT_CA_CERTIFICATE_RAW: &str = "tls_root_ca_certificate_raw"; + pub const TLS_ROOT_CA_CERTIFICATE_FILE: &str = "root_ca_certificate_file"; + pub const TLS_ROOT_CA_CERTIFICATE_RAW: &str = "root_ca_certificate_raw"; - pub const TLS_SERVER_PRIVATE_KEY_FILE: &str = ZN_TLS_SERVER_PRIVATE_KEY_STR; - pub const TLS_SERVER_PRIVATE_KEY_RAW: &str = "tls_server_private_key_raw"; + pub const TLS_SERVER_PRIVATE_KEY_FILE: &str = "server_private_key_file"; + pub const TLS_SERVER_PRIVATE_KEY_RAW: &str = "server_private_key_raw"; - pub const TLS_SERVER_CERTIFICATE_FILE: &str = ZN_TLS_SERVER_CERTIFICATE_STR; - pub const TLS_SERVER_CERTIFICATE_RAW: &str = "tls_server_certificate_raw"; + pub const TLS_SERVER_CERTIFICATE_FILE: &str = "server_certificate_file"; + pub const TLS_SERVER_CERTIFICATE_RAW: &str = "server_certificate_raw"; - pub const TLS_CLIENT_PRIVATE_KEY_FILE: &str = ZN_TLS_CLIENT_PRIVATE_KEY_STR; - pub const TLS_CLIENT_PRIVATE_KEY_RAW: &str = "tls_client_private_key_raw"; + pub const TLS_CLIENT_PRIVATE_KEY_FILE: &str = "client_private_key_file"; + pub const TLS_CLIENT_PRIVATE_KEY_RAW: &str = "client_private_key_raw"; - pub const TLS_CLIENT_CERTIFICATE_FILE: &str = ZN_TLS_CLIENT_CERTIFICATE_STR; - pub const TLS_CLIENT_CERTIFICATE_RAW: &str = "tls_client_certificate_raw"; + pub const TLS_CLIENT_CERTIFICATE_FILE: &str = "client_certificate_file"; + pub const TLS_CLIENT_CERTIFICATE_RAW: &str = "client_certificate_raw"; - pub const TLS_CLIENT_AUTH: &str = ZN_TLS_CLIENT_AUTH_STR; - pub const TLS_CLIENT_AUTH_DEFAULT: &str = ZN_TLS_CLIENT_AUTH_DEFAULT; + pub const TLS_CLIENT_AUTH: &str = "client_auth"; - pub const TLS_SERVER_NAME_VERIFICATION: &str = ZN_TLS_SERVER_NAME_VERIFICATION_STR; - pub const TLS_SERVER_NAME_VERIFICATION_DEFAULT: &str = ZN_TLS_SERVER_NAME_VERIFICATION_DEFAULT; + pub const TLS_SERVER_NAME_VERIFICATION: &str = "server_name_verification"; } pub async fn get_tls_addr(address: &Address<'_>) -> ZResult { diff --git a/io/zenoh-links/zenoh-link-tls/src/unicast.rs b/io/zenoh-links/zenoh-link-tls/src/unicast.rs index e7777df393..ff8d68e6bd 100644 --- a/io/zenoh-links/zenoh-link-tls/src/unicast.rs +++ b/io/zenoh-links/zenoh-link-tls/src/unicast.rs @@ -16,10 +16,13 @@ use crate::{ verify::WebPkiVerifierAnyServerName, TLS_ACCEPT_THROTTLE_TIME, TLS_DEFAULT_MTU, TLS_LINGER_TIMEOUT, TLS_LOCATOR_PREFIX, }; -use async_rustls::rustls::server::AllowAnyAuthenticatedClient; -use async_rustls::rustls::version::TLS13; -pub use async_rustls::rustls::*; -use async_rustls::{TlsAcceptor, TlsConnector, TlsStream}; +use async_rustls::{ + rustls::{ + server::AllowAnyAuthenticatedClient, version::TLS13, Certificate, ClientConfig, + OwnedTrustAnchor, PrivateKey, RootCertStore, ServerConfig, + }, + TlsAcceptor, TlsConnector, TlsStream, +}; use async_std::fs; use async_std::net::{SocketAddr, TcpListener, TcpStream}; use async_std::prelude::FutureExt; @@ -513,7 +516,12 @@ struct TlsServerConfig { impl TlsServerConfig { pub async fn new(config: &Config<'_>) -> ZResult { - let mut client_auth: bool = TLS_CLIENT_AUTH_DEFAULT.parse().unwrap(); + let tls_server_client_auth: bool = match config.get(TLS_CLIENT_AUTH) { + Some(s) => s + .parse() + .map_err(|_| zerror!("Unknown client auth argument: {}", s))?, + None => false, + }; let tls_server_private_key = TlsServerConfig::load_tls_private_key(config).await?; let tls_server_certificate = TlsServerConfig::load_tls_certificate(config).await?; @@ -543,11 +551,7 @@ impl TlsServerConfig { .map_err(|e| zerror!(e)) .map(|mut certs| certs.drain(..).map(Certificate).collect())?; - if let Some(value) = config.get(TLS_CLIENT_AUTH) { - client_auth = value.parse()? - } - - let sc = if client_auth { + let sc = if tls_server_client_auth { let root_cert_store = load_trust_anchors(config)?.map_or_else( || { Err(zerror!( @@ -599,19 +603,25 @@ struct TlsClientConfig { impl TlsClientConfig { pub async fn new(config: &Config<'_>) -> ZResult { - let client_auth: bool = config - .get(TLS_CLIENT_AUTH) - .unwrap_or(TLS_CLIENT_AUTH_DEFAULT) - .parse()?; - - let server_name_verification: bool = config - .get(TLS_SERVER_NAME_VERIFICATION) - .unwrap_or(TLS_SERVER_NAME_VERIFICATION_DEFAULT) - .parse()?; - - if !server_name_verification { - log::warn!("Skipping name verification of servers"); - } + let tls_client_server_auth: bool = match config.get(TLS_CLIENT_AUTH) { + Some(s) => s + .parse() + .map_err(|_| zerror!("Unknown client auth argument: {}", s))?, + None => false, + }; + + let tls_server_name_verification: bool = match config.get(TLS_SERVER_NAME_VERIFICATION) { + Some(s) => { + let s: bool = s + .parse() + .map_err(|_| zerror!("Unknown server name verification argument: {}", s))?; + if s { + log::warn!("Skipping name verification of servers"); + } + s + } + None => false, + }; // Allows mixed user-generated CA and webPKI CA log::debug!("Loading default Web PKI certificates."); @@ -624,7 +634,7 @@ impl TlsClientConfig { root_cert_store.add_trust_anchors(custom_root_cert.roots.into_iter()); } - let cc = if client_auth { + let cc = if tls_client_server_auth { log::debug!("Loading client authentication key and certificate..."); let tls_client_private_key = TlsClientConfig::load_tls_private_key(config).await?; let tls_client_certificate = TlsClientConfig::load_tls_certificate(config).await?; @@ -654,9 +664,9 @@ impl TlsClientConfig { .with_safe_default_cipher_suites() .with_safe_default_kx_groups() .with_protocol_versions(&[&TLS13]) - .expect("Config parameters should be valid"); + .map_err(|e| zerror!("Config parameters should be valid: {}", e))?; - if server_name_verification { + if tls_server_name_verification { builder .with_root_certificates(root_cert_store) .with_client_auth_cert(certs, keys.remove(0)) @@ -667,10 +677,10 @@ impl TlsClientConfig { ))) .with_client_auth_cert(certs, keys.remove(0)) } - .expect("bad certificate/key") + .map_err(|e| zerror!("Bad certificate/key: {}", e))? } else { let builder = ClientConfig::builder().with_safe_defaults(); - if server_name_verification { + if tls_server_name_verification { builder .with_root_certificates(root_cert_store) .with_no_client_auth() diff --git a/io/zenoh-transport/Cargo.toml b/io/zenoh-transport/Cargo.toml index badd5ca94a..7476da7196 100644 --- a/io/zenoh-transport/Cargo.toml +++ b/io/zenoh-transport/Cargo.toml @@ -30,8 +30,10 @@ shared-memory = [ "zenoh-shm", "zenoh-codec/shared-memory", ] -auth_pubkey = ["rsa"] -auth_usrpwd = [] +auth_pubkey = ["transport_auth", "rsa"] +auth_usrpwd = ["transport_auth"] +transport_auth = [] +transport_multilink = ["auth_pubkey"] transport_quic = ["zenoh-link/transport_quic"] transport_tcp = ["zenoh-link/transport_tcp"] transport_tls = ["zenoh-link/transport_tls"] @@ -40,7 +42,9 @@ transport_unixsock-stream = ["zenoh-link/transport_unixsock-stream"] transport_ws = ["zenoh-link/transport_ws"] transport_serial = ["zenoh-link/transport_serial"] transport_compression = [] -stats = [] +transport_shm = ["zenoh-link/transport_shm"] +stats = ["zenoh-protocol/stats"] +test = [] unstable = [] [dependencies] @@ -55,9 +59,9 @@ paste = { workspace = true } rand = { workspace = true, features = ["default"] } ringbuffer-spsc = { workspace = true } rsa = { workspace = true, optional = true } +sha3 = { workspace = true } serde = { workspace = true, features = ["default"] } zenoh-buffers = { workspace = true } -zenoh-cfg-properties = { workspace = true } zenoh-codec = { workspace = true } zenoh-collections = { workspace = true } zenoh-config = { workspace = true } @@ -74,3 +78,5 @@ zenoh-util = { workspace = true } env_logger = { workspace = true } panic-message = { workspace = true } zenoh-protocol = { workspace = true, features = ["test"] } +zenoh-transport = { workspace = true, features = ["test", "transport_multilink"] } + diff --git a/io/zenoh-transport/src/common/batch.rs b/io/zenoh-transport/src/common/batch.rs index c753ed4078..6fc2051242 100644 --- a/io/zenoh-transport/src/common/batch.rs +++ b/io/zenoh-transport/src/common/batch.rs @@ -17,11 +17,13 @@ use zenoh_buffers::{ writer::{BacktrackableWriter, DidntWrite, HasWriter, Writer}, BBuf, ZBufReader, }; -use zenoh_codec::{WCodec, Zenoh060}; +use zenoh_codec::{WCodec, Zenoh080}; use zenoh_protocol::{ - core::{Channel, Reliability, ZInt}, - transport::{FrameHeader, FrameKind, TransportMessage}, - zenoh::ZenohMessage, + core::Reliability, + network::NetworkMessage, + transport::{ + fragment::FragmentHeader, frame::FrameHeader, BatchSize, TransportMessage, TransportSn, + }, }; const LENGTH_BYTES: [u8; 2] = u16::MIN.to_be_bytes(); @@ -46,8 +48,8 @@ pub(crate) enum CurrentFrame { #[derive(Clone, Copy, Debug)] pub(crate) struct LatestSn { - pub(crate) reliable: Option, - pub(crate) best_effort: Option, + pub(crate) reliable: Option, + pub(crate) best_effort: Option, } impl LatestSn { @@ -105,7 +107,7 @@ pub(crate) struct WBatch { } impl WBatch { - pub(crate) fn new(size: u16, is_streamed: bool) -> Self { + pub(crate) fn new(size: BatchSize, is_streamed: bool) -> Self { let mut batch = Self { buffer: BBuf::with_capacity(size as usize), is_streamed, @@ -132,10 +134,10 @@ impl WBatch { /// Get the total number of bytes that have been serialized on the [`SerializationBatch`][SerializationBatch]. #[inline(always)] - pub(crate) fn len(&self) -> u16 { - let len = self.buffer.len() as u16; + pub(crate) fn len(&self) -> BatchSize { + let len = self.buffer.len() as BatchSize; if self.is_streamed() { - len - (LENGTH_BYTES.len() as u16) + len - (LENGTH_BYTES.len() as BatchSize) } else { len } @@ -194,7 +196,7 @@ impl Encode<&TransportMessage> for &mut WBatch { let mut writer = self.buffer.writer(); let mark = writer.mark(); - let codec = Zenoh060::default(); + let codec = Zenoh080::new(); codec.write(&mut writer, message).map_err(|e| { // Revert the write operation writer.rewind(mark); @@ -217,15 +219,15 @@ pub(crate) enum WError { DidntWrite, } -impl Encode<&ZenohMessage> for &mut WBatch { +impl Encode<&NetworkMessage> for &mut WBatch { type Output = Result<(), WError>; - /// Try to serialize a [`ZenohMessage`][ZenohMessage] on the [`SerializationBatch`][SerializationBatch]. + /// Try to serialize a [`NetworkMessage`][NetworkMessage] on the [`SerializationBatch`][SerializationBatch]. /// /// # Arguments - /// * `message` - The [`ZenohMessage`][ZenohMessage] to serialize. + /// * `message` - The [`NetworkMessage`][NetworkMessage] to serialize. /// - fn encode(self, message: &ZenohMessage) -> Self::Output { + fn encode(self, message: &NetworkMessage) -> Self::Output { // Eventually update the current frame and sn based on the current status if let (CurrentFrame::Reliable, false) | (CurrentFrame::BestEffort, true) @@ -239,7 +241,7 @@ impl Encode<&ZenohMessage> for &mut WBatch { let mut writer = self.buffer.writer(); let mark = writer.mark(); - let codec = Zenoh060::default(); + let codec = Zenoh080::new(); codec.write(&mut writer, message).map_err(|_| { // Revert the write operation writer.rewind(mark); @@ -248,28 +250,23 @@ impl Encode<&ZenohMessage> for &mut WBatch { } } -impl Encode<(&ZenohMessage, Channel, ZInt)> for &mut WBatch { +impl Encode<(&NetworkMessage, FrameHeader)> for &mut WBatch { type Output = Result<(), DidntWrite>; - /// Try to serialize a [`ZenohMessage`][ZenohMessage] on the [`SerializationBatch`][SerializationBatch]. + /// Try to serialize a [`NetworkMessage`][NetworkMessage] on the [`SerializationBatch`][SerializationBatch]. /// /// # Arguments - /// * `message` - The [`ZenohMessage`][ZenohMessage] to serialize. + /// * `message` - The [`NetworkMessage`][NetworkMessage] to serialize. /// - fn encode(self, message: (&ZenohMessage, Channel, ZInt)) -> Self::Output { - let (message, channel, sn) = message; + fn encode(self, message: (&NetworkMessage, FrameHeader)) -> Self::Output { + let (message, frame) = message; // Mark the write operation let mut writer = self.buffer.writer(); let mark = writer.mark(); - let codec = Zenoh060::default(); + let codec = Zenoh080::new(); // Write the frame header - let frame = FrameHeader { - channel, - sn, - kind: FrameKind::Messages, - }; codec.write(&mut writer, &frame).map_err(|e| { // Revert the write operation writer.rewind(mark); @@ -282,13 +279,13 @@ impl Encode<(&ZenohMessage, Channel, ZInt)> for &mut WBatch { e })?; // Update the frame - self.current_frame = match frame.channel.reliability { + self.current_frame = match frame.reliability { Reliability::Reliable => { - self.latest_sn.reliable = Some(sn); + self.latest_sn.reliable = Some(frame.sn); CurrentFrame::Reliable } Reliability::BestEffort => { - self.latest_sn.best_effort = Some(sn); + self.latest_sn.best_effort = Some(frame.sn); CurrentFrame::BestEffort } }; @@ -296,7 +293,7 @@ impl Encode<(&ZenohMessage, Channel, ZInt)> for &mut WBatch { } } -impl Encode<(&mut ZBufReader<'_>, Channel, ZInt)> for &mut WBatch { +impl Encode<(&mut ZBufReader<'_>, FragmentHeader)> for &mut WBatch { type Output = Result; /// Try to serialize a [`ZenohMessage`][ZenohMessage] on the [`SerializationBatch`][SerializationBatch]. @@ -304,23 +301,17 @@ impl Encode<(&mut ZBufReader<'_>, Channel, ZInt)> for &mut WBatch { /// # Arguments /// * `message` - The [`ZenohMessage`][ZenohMessage] to serialize. /// - fn encode(self, message: (&mut ZBufReader<'_>, Channel, ZInt)) -> Self::Output { - let (reader, channel, sn) = message; + fn encode(self, message: (&mut ZBufReader<'_>, FragmentHeader)) -> Self::Output { + let (reader, mut fragment) = message; let mut writer = self.buffer.writer(); - let codec = Zenoh060::default(); + let codec = Zenoh080::new(); // Mark the buffer for the writing operation let mark = writer.mark(); - // Serialize first assuming is some fragment - let mut frame = FrameHeader { - channel, - sn, - kind: FrameKind::SomeFragment, - }; // Write the frame header - codec.write(&mut writer, &frame).map_err(|e| { + codec.write(&mut writer, &fragment).map_err(|e| { // Revert the write operation writer.rewind(mark); e @@ -331,9 +322,9 @@ impl Encode<(&mut ZBufReader<'_>, Channel, ZInt)> for &mut WBatch { // Revert the buffer writer.rewind(mark); // It is really the finally fragment, reserialize the header - frame.kind = FrameKind::LastFragment; + fragment.more = false; // Write the frame header - codec.write(&mut writer, &frame).map_err(|e| { + codec.write(&mut writer, &fragment).map_err(|e| { // Revert the write operation writer.rewind(mark); e @@ -341,7 +332,7 @@ impl Encode<(&mut ZBufReader<'_>, Channel, ZInt)> for &mut WBatch { } // Write the fragment - reader.siphon(&mut *writer).map_err(|_| { + reader.siphon(&mut writer).map_err(|_| { // Revert the write operation writer.rewind(mark); DidntWrite @@ -354,63 +345,73 @@ mod tests { use super::*; use zenoh_buffers::ZBuf; use zenoh_protocol::{ - core::{Channel, CongestionControl, Priority, Reliability}, - transport::TransportMessage, - zenoh::ZenohMessage, + core::{CongestionControl, Encoding, Priority, Reliability, WireExpr}, + network::{ext, Push}, + transport::{ + frame::{self, FrameHeader}, + KeepAlive, TransportMessage, + }, + zenoh::{PushBody, Put}, }; #[test] fn serialization_batch() { let mut batch = WBatch::new(u16::MAX, true); - let tmsg = TransportMessage::make_keep_alive(None, None); - let mut zmsg = ZenohMessage::make_data( - 0.into(), - ZBuf::from(vec![0u8; 8]), - Channel { - priority: Priority::default(), - reliability: Reliability::Reliable, - }, - CongestionControl::Block, - None, - None, - None, - None, - ); + let tmsg: TransportMessage = KeepAlive.into(); + let nmsg: NetworkMessage = Push { + wire_expr: WireExpr::empty(), + ext_qos: ext::QoSType::new(Priority::default(), CongestionControl::Block, false), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + payload: PushBody::Put(Put { + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + payload: ZBuf::from(vec![0u8; 8]), + }), + } + .into(); let mut tmsgs_in = vec![]; - let mut zmsgs_in = vec![]; + let mut nmsgs_in = vec![]; // Serialize assuming there is already a frame batch.clear(); - assert!(batch.encode(&zmsg).is_err()); + assert!(batch.encode(&nmsg).is_err()); assert_eq!(batch.len(), 0); - // Serialize with a frame - batch.encode((&zmsg, zmsg.channel, 0)).unwrap(); - assert_ne!(batch.len(), 0); - zmsgs_in.push(zmsg.clone()); + let mut frame = FrameHeader { + reliability: Reliability::Reliable, + sn: 0, + ext_qos: frame::ext::QoSType::default(), + }; - // Change reliability - zmsg.channel.reliability = Reliability::BestEffort; - assert!(batch.encode(&zmsg).is_err()); + // Serialize with a frame + batch.encode((&nmsg, frame)).unwrap(); assert_ne!(batch.len(), 0); + nmsgs_in.push(nmsg.clone()); - batch.encode((&zmsg, zmsg.channel, 0)).unwrap(); + frame.reliability = Reliability::BestEffort; + batch.encode((&nmsg, frame)).unwrap(); assert_ne!(batch.len(), 0); - zmsgs_in.push(zmsg.clone()); + nmsgs_in.push(nmsg.clone()); // Transport batch.encode(&tmsg).unwrap(); tmsgs_in.push(tmsg.clone()); // Serialize assuming there is already a frame - assert!(batch.encode(&zmsg).is_err()); + assert!(batch.encode(&nmsg).is_err()); assert_ne!(batch.len(), 0); // Serialize with a frame - batch.encode((&zmsg, zmsg.channel, 1)).unwrap(); + frame.sn = 1; + batch.encode((&nmsg, frame)).unwrap(); assert_ne!(batch.len(), 0); - zmsgs_in.push(zmsg.clone()); + nmsgs_in.push(nmsg.clone()); } } diff --git a/io/zenoh-transport/src/common/defragmentation.rs b/io/zenoh-transport/src/common/defragmentation.rs index 105752c893..be734cad45 100644 --- a/io/zenoh-transport/src/common/defragmentation.rs +++ b/io/zenoh-transport/src/common/defragmentation.rs @@ -13,10 +13,11 @@ // use super::seq_num::SeqNum; use zenoh_buffers::{reader::HasReader, SplitBuffer, ZBuf, ZSlice}; -use zenoh_codec::{RCodec, Zenoh060Reliability}; +use zenoh_codec::{RCodec, Zenoh080Reliability}; use zenoh_protocol::{ - core::{Reliability, ZInt}, - zenoh::ZenohMessage, + core::{Bits, Reliability}, + network::NetworkMessage, + transport::TransportSn, }; use zenoh_result::{bail, ZResult}; @@ -32,13 +33,13 @@ pub(crate) struct DefragBuffer { impl DefragBuffer { pub(crate) fn make( reliability: Reliability, - sn_resolution: ZInt, + resolution: Bits, capacity: usize, ) -> ZResult { let db = DefragBuffer { reliability, - sn: SeqNum::make(0, sn_resolution)?, - buffer: ZBuf::default(), + sn: SeqNum::make(0, resolution)?, + buffer: ZBuf::empty(), capacity, len: 0, }; @@ -57,11 +58,11 @@ impl DefragBuffer { } #[inline(always)] - pub(crate) fn sync(&mut self, sn: ZInt) -> ZResult<()> { + pub(crate) fn sync(&mut self, sn: TransportSn) -> ZResult<()> { self.sn.set(sn) } - pub(crate) fn push(&mut self, sn: ZInt, zslice: ZSlice) -> ZResult<()> { + pub(crate) fn push(&mut self, sn: TransportSn, zslice: ZSlice) -> ZResult<()> { if sn != self.sn.get() { self.clear(); bail!("Expected SN {}, received {}", self.sn.get(), sn) @@ -85,10 +86,10 @@ impl DefragBuffer { } #[inline(always)] - pub(crate) fn defragment(&mut self) -> Option { + pub(crate) fn defragment(&mut self) -> Option { let mut reader = self.buffer.reader(); - let rcodec = Zenoh060Reliability::new(self.reliability); - let res: Option = rcodec.read(&mut reader).ok(); + let rcodec = Zenoh080Reliability::new(self.reliability); + let res: Option = rcodec.read(&mut reader).ok(); self.clear(); res } diff --git a/io/zenoh-transport/src/common/mod.rs b/io/zenoh-transport/src/common/mod.rs index f13936f0e6..0837ced4f7 100644 --- a/io/zenoh-transport/src/common/mod.rs +++ b/io/zenoh-transport/src/common/mod.rs @@ -12,9 +12,9 @@ // ZettaScale Zenoh Team, // pub(crate) mod batch; -pub(crate) mod conduit; pub(crate) mod defragmentation; pub(crate) mod pipeline; +pub(crate) mod priority; pub(crate) mod seq_num; #[cfg(feature = "stats")] -pub(crate) mod stats; +pub mod stats; diff --git a/io/zenoh-transport/src/common/pipeline.rs b/io/zenoh-transport/src/common/pipeline.rs index cecbf42c18..47c5ef4a4d 100644 --- a/io/zenoh-transport/src/common/pipeline.rs +++ b/io/zenoh-transport/src/common/pipeline.rs @@ -1,5 +1,3 @@ -use crate::common::batch::WError; - // // Copyright (c) 2023 ZettaScale Technology // @@ -13,9 +11,8 @@ use crate::common::batch::WError; // Contributors: // ZettaScale Zenoh Team, // -// use super::batch::SerializationBatch; -use super::batch::{Encode, WBatch}; -use super::conduit::{TransportChannelTx, TransportConduitTx}; +use super::batch::{Encode, WBatch, WError}; +use super::priority::{TransportChannelTx, TransportPriorityTx}; use async_std::prelude::FutureExt; use flume::{bounded, Receiver, Sender}; use ringbuffer_spsc::{RingBuffer, RingBufferReader, RingBufferWriter}; @@ -28,13 +25,18 @@ use zenoh_buffers::{ writer::HasWriter, ZBuf, }; -use zenoh_codec::{WCodec, Zenoh060}; +use zenoh_codec::{WCodec, Zenoh080}; use zenoh_config::QueueSizeConf; use zenoh_core::zlock; +use zenoh_protocol::core::Reliability; +use zenoh_protocol::network::NetworkMessage; use zenoh_protocol::{ - core::{Channel, Priority}, - transport::TransportMessage, - zenoh::ZenohMessage, + core::Priority, + transport::{ + fragment::FragmentHeader, + frame::{self, FrameHeader}, + BatchSize, TransportMessage, + }, }; // It's faster to work directly with nanoseconds. @@ -70,7 +72,7 @@ struct StageInOut { impl StageInOut { #[inline] - fn notify(&self, bytes: u16) { + fn notify(&self, bytes: BatchSize) { self.bytes.store(bytes, Ordering::Relaxed); if !self.backoff.load(Ordering::Relaxed) { let _ = self.n_out_w.try_send(()); @@ -88,7 +90,7 @@ impl StageInOut { // Inner structure containing mutexes for current serialization batch and SNs struct StageInMutex { current: Arc>>, - conduit: TransportConduitTx, + priority: TransportPriorityTx, } impl StageInMutex { @@ -100,9 +102,9 @@ impl StageInMutex { #[inline] fn channel(&self, is_reliable: bool) -> MutexGuard<'_, TransportChannelTx> { if is_reliable { - zlock!(self.conduit.reliable) + zlock!(self.priority.reliable) } else { - zlock!(self.conduit.best_effort) + zlock!(self.priority.best_effort) } } } @@ -116,7 +118,7 @@ struct StageIn { } impl StageIn { - fn push_zenoh_message(&mut self, msg: &mut ZenohMessage, priority: Priority) -> bool { + fn push_network_message(&mut self, msg: &mut NetworkMessage, priority: Priority) -> bool { // Lock the current serialization batch. let mut c_guard = self.mutex.current(); @@ -175,18 +177,19 @@ impl StageIn { // Lock the channel. We are the only one that will be writing on it. let mut tch = self.mutex.channel(msg.is_reliable()); - // Create the channel - let channel = Channel { - reliability: msg.channel.reliability, - priority, - }; - // Retrieve the next SN - let mut sn = tch.sn.get(); + let sn = tch.sn.get(); + + // The Frame + let frame = FrameHeader { + reliability: Reliability::Reliable, // TODO + sn, + ext_qos: frame::ext::QoSType::new(priority), + }; if let WError::NewFrame = e { // Attempt a serialization with a new frame - if batch.encode((&*msg, channel, sn)).is_ok() { + if batch.encode((&*msg, frame)).is_ok() { zretok!(batch); }; } @@ -198,7 +201,7 @@ impl StageIn { } // Attempt a second serialization on fully empty batch - if batch.encode((&*msg, channel, sn)).is_ok() { + if batch.encode((&*msg, frame)).is_ok() { zretok!(batch); }; @@ -211,10 +214,16 @@ impl StageIn { self.fragbuf.clear(); let mut writer = self.fragbuf.writer(); - let codec = Zenoh060::default(); + let codec = Zenoh080::new(); codec.write(&mut writer, &*msg).unwrap(); // Fragment the whole message + let mut fragment = FragmentHeader { + reliability: frame.reliability, + more: true, + sn, + ext_qos: frame.ext_qos, + }; let mut reader = self.fragbuf.reader(); while reader.can_read() { // Get the current serialization batch @@ -222,10 +231,10 @@ impl StageIn { batch = zgetbatch_rets!(true); // Serialize the message fragmnet - match batch.encode((&mut reader, channel, sn)) { + match batch.encode((&mut reader, fragment)) { Ok(_) => { // Update the SN - sn = tch.sn.get(); + fragment.sn = tch.sn.get(); // Move the serialization batch into the OUT pipeline self.s_out.move_batch(batch); } @@ -318,7 +327,7 @@ enum Pull { #[derive(Clone)] struct Backoff { retry_time: NanoSeconds, - last_bytes: u16, + last_bytes: BatchSize, bytes: Arc, backoff: Arc, } @@ -475,7 +484,7 @@ impl StageOut { #[derive(Debug, Clone, PartialEq, Eq)] pub(crate) struct TransmissionPipelineConf { pub(crate) is_streamed: bool, - pub(crate) batch_size: u16, + pub(crate) batch_size: BatchSize, pub(crate) queue_size: [usize; Priority::NUM], pub(crate) backoff: Duration, } @@ -484,7 +493,7 @@ impl Default for TransmissionPipelineConf { fn default() -> Self { Self { is_streamed: false, - batch_size: u16::MAX, + batch_size: BatchSize::MAX, queue_size: [1; Priority::NUM], backoff: Duration::from_micros(1), } @@ -497,13 +506,13 @@ impl TransmissionPipeline { // A MPSC pipeline pub(crate) fn make( config: TransmissionPipelineConf, - conduit: &[TransportConduitTx], + priority: &[TransportPriorityTx], ) -> (TransmissionPipelineProducer, TransmissionPipelineConsumer) { let mut stage_in = vec![]; let mut stage_out = vec![]; let default_queue_size = [config.queue_size[Priority::default() as usize]]; - let size_iter = if conduit.len() == 1 { + let size_iter = if priority.len() == 1 { default_queue_size.iter() } else { config.queue_size.iter() @@ -546,9 +555,9 @@ impl TransmissionPipeline { }, mutex: StageInMutex { current: current.clone(), - conduit: conduit[prio].clone(), + priority: priority[prio].clone(), }, - fragbuf: ZBuf::default(), + fragbuf: ZBuf::empty(), })); // The stage out for this priority @@ -586,16 +595,17 @@ pub(crate) struct TransmissionPipelineProducer { impl TransmissionPipelineProducer { #[inline] - pub(crate) fn push_zenoh_message(&self, mut msg: ZenohMessage) -> bool { + pub(crate) fn push_network_message(&self, mut msg: NetworkMessage) -> bool { // If the queue is not QoS, it means that we only have one priority with index 0. let (idx, priority) = if self.stage_in.len() > 1 { - (msg.channel.priority as usize, msg.channel.priority) + let priority = msg.priority(); + (priority as usize, priority) } else { (0, Priority::default()) }; // Lock the channel. We are the only one that will be writing on it. let mut queue = zlock!(self.stage_in[idx]); - queue.push_zenoh_message(&mut msg, priority) + queue.push_network_message(&mut msg, priority) } #[inline] @@ -621,7 +631,7 @@ impl TransmissionPipelineProducer { // Unblock waiting pullers for ig in in_guards.iter_mut() { - ig.s_out.notify(u16::MAX); + ig.s_out.notify(BatchSize::MAX); } } } @@ -707,12 +717,12 @@ mod tests { reader::{DidntRead, HasReader}, ZBuf, }; - use zenoh_codec::{RCodec, Zenoh060}; + use zenoh_codec::{RCodec, Zenoh080}; use zenoh_protocol::{ - core::{Channel, CongestionControl, Priority, Reliability, ZInt}, - defaults::{BATCH_SIZE, SEQ_NUM_RES}, - transport::{Frame, FramePayload, TransportBody}, - zenoh::ZenohMessage, + core::{Bits, CongestionControl, Encoding, Priority}, + network::{ext, Push}, + transport::{BatchSize, Fragment, Frame, TransportBody, TransportSn}, + zenoh::{PushBody, Put}, }; const SLEEP: Duration = Duration::from_millis(100); @@ -720,7 +730,7 @@ mod tests { const CONFIG: TransmissionPipelineConf = TransmissionPipelineConf { is_streamed: true, - batch_size: BATCH_SIZE, + batch_size: BatchSize::MAX, queue_size: [1; Priority::NUM], backoff: Duration::from_micros(1), }; @@ -731,33 +741,33 @@ mod tests { // Send reliable messages let key = "test".into(); let payload = ZBuf::from(vec![0_u8; payload_size]); - let data_info = None; - let routing_context = None; - let reply_context = None; - let attachment = None; - let channel = Channel { - priority: Priority::Control, - reliability: Reliability::Reliable, - }; - let congestion_control = CongestionControl::Block; - - let message = ZenohMessage::make_data( - key, - payload, - channel, - congestion_control, - data_info, - routing_context, - reply_context, - attachment, - ); + + let message: NetworkMessage = Push { + wire_expr: key, + ext_qos: ext::QoSType::new(Priority::Control, CongestionControl::Block, false), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + payload: PushBody::Put(Put { + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + payload, + }), + } + .into(); println!( "Pipeline Flow [>>>]: Sending {num_msg} messages with payload size of {payload_size} bytes" ); for i in 0..num_msg { - println!("Pipeline Flow [>>>]: Pushed {} msgs", i + 1); - queue.push_zenoh_message(message.clone()); + println!( + "Pipeline Flow [>>>]: Pushed {} msgs ({payload_size} bytes)", + i + 1 + ); + queue.push_network_message(message.clone()); } } @@ -775,24 +785,22 @@ mod tests { let bytes = batch.as_bytes(); // Deserialize the messages let mut reader = bytes.reader(); - let codec = Zenoh060::default(); + let codec = Zenoh080::new(); loop { let res: Result = codec.read(&mut reader); match res { Ok(msg) => { match msg.body { - TransportBody::Frame(Frame { payload, .. }) => match payload { - FramePayload::Messages { messages } => { - msgs += messages.len(); - } - FramePayload::Fragment { is_final, .. } => { - fragments += 1; - if is_final { - msgs += 1; - } + TransportBody::Frame(Frame { payload, .. }) => { + msgs += payload.len() + } + TransportBody::Fragment(Fragment { more, .. }) => { + fragments += 1; + if !more { + msgs += 1; } - }, + } _ => { msgs += 1; } @@ -812,9 +820,9 @@ mod tests { ); } - // Pipeline conduits - let tct = TransportConduitTx::make(SEQ_NUM_RES).unwrap(); - let conduits = vec![tct]; + // Pipeline priorities + let tct = TransportPriorityTx::make(Bits::from(TransportSn::MAX)).unwrap(); + let priorities = vec![tct]; // Total amount of bytes to send in each test let bytes: usize = 100_000_000; @@ -824,7 +832,7 @@ mod tests { task::block_on(async { for ps in payload_sizes.iter() { - if ZInt::try_from(*ps).is_err() { + if u64::try_from(*ps).is_err() { break; } @@ -833,7 +841,7 @@ mod tests { let (producer, consumer) = TransmissionPipeline::make( TransmissionPipelineConf::default(), - conduits.as_slice(), + priorities.as_slice(), ); let t_c = task::spawn(async move { @@ -862,26 +870,23 @@ mod tests { // Send reliable messages let key = "test".into(); let payload = ZBuf::from(vec![0_u8; payload_size]); - let channel = Channel { - priority: Priority::Control, - reliability: Reliability::Reliable, - }; - let congestion_control = CongestionControl::Block; - let data_info = None; - let routing_context = None; - let reply_context = None; - let attachment = None; - - let message = ZenohMessage::make_data( - key, - payload, - channel, - congestion_control, - data_info, - routing_context, - reply_context, - attachment, - ); + + let message: NetworkMessage = Push { + wire_expr: key, + ext_qos: ext::QoSType::new(Priority::Control, CongestionControl::Block, false), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + payload: PushBody::Put(Put { + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + payload, + }), + } + .into(); // The last push should block since there shouldn't any more batches // available for serialization. @@ -890,7 +895,7 @@ mod tests { println!( "Pipeline Blocking [>>>]: ({id}) Scheduling message #{i} with payload size of {payload_size} bytes" ); - queue.push_zenoh_message(message.clone()); + queue.push_network_message(message.clone()); let c = counter.fetch_add(1, Ordering::AcqRel); println!( "Pipeline Blocking [>>>]: ({}) Scheduled message #{} (tot {}) with payload size of {} bytes", @@ -901,10 +906,10 @@ mod tests { } // Pipeline - let tct = TransportConduitTx::make(SEQ_NUM_RES).unwrap(); - let conduits = vec![tct]; + let tct = TransportPriorityTx::make(Bits::from(TransportSn::MAX)).unwrap(); + let priorities = vec![tct]; let (producer, mut consumer) = - TransmissionPipeline::make(TransmissionPipelineConf::default(), conduits.as_slice()); + TransmissionPipeline::make(TransmissionPipelineConf::default(), priorities.as_slice()); let counter = Arc::new(AtomicUsize::new(0)); @@ -953,9 +958,9 @@ mod tests { #[ignore] fn tx_pipeline_thr() { // Queue - let tct = TransportConduitTx::make(SEQ_NUM_RES).unwrap(); - let conduits = vec![tct]; - let (producer, mut consumer) = TransmissionPipeline::make(CONFIG, conduits.as_slice()); + let tct = TransportPriorityTx::make(Bits::from(TransportSn::MAX)).unwrap(); + let priorities = vec![tct]; + let (producer, mut consumer) = TransmissionPipeline::make(CONFIG, priorities.as_slice()); let count = Arc::new(AtomicUsize::new(0)); let size = Arc::new(AtomicUsize::new(0)); @@ -972,31 +977,32 @@ mod tests { // Send reliable messages let key = "pipeline/thr".into(); let payload = ZBuf::from(vec![0_u8; *size]); - let channel = Channel { - priority: Priority::Control, - reliability: Reliability::Reliable, - }; - let congestion_control = CongestionControl::Block; - let data_info = None; - let routing_context = None; - let reply_context = None; - let attachment = None; - - let message = ZenohMessage::make_data( - key, - payload, - channel, - congestion_control, - data_info, - routing_context, - reply_context, - attachment, - ); + + let message: NetworkMessage = Push { + wire_expr: key, + ext_qos: ext::QoSType::new( + Priority::Control, + CongestionControl::Block, + false, + ), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + payload: PushBody::Put(Put { + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + payload, + }), + } + .into(); let duration = Duration::from_millis(5_500); let start = Instant::now(); while start.elapsed() < duration { - producer.push_zenoh_message(message.clone()); + producer.push_network_message(message.clone()); } } } diff --git a/io/zenoh-transport/src/common/conduit.rs b/io/zenoh-transport/src/common/priority.rs similarity index 64% rename from io/zenoh-transport/src/common/conduit.rs rename to io/zenoh-transport/src/common/priority.rs index baaab7cceb..8644cdacb7 100644 --- a/io/zenoh-transport/src/common/conduit.rs +++ b/io/zenoh-transport/src/common/priority.rs @@ -15,7 +15,10 @@ use super::defragmentation::DefragBuffer; use super::seq_num::{SeqNum, SeqNumGenerator}; use std::sync::{Arc, Mutex}; use zenoh_core::zlock; -use zenoh_protocol::core::{ConduitSn, Reliability, ZInt}; +use zenoh_protocol::{ + core::{Bits, Reliability}, + transport::{PrioritySn, TransportSn}, +}; use zenoh_result::ZResult; #[derive(Debug)] @@ -24,14 +27,14 @@ pub(crate) struct TransportChannelTx { } impl TransportChannelTx { - pub(crate) fn make(sn_resolution: ZInt) -> ZResult { + pub(crate) fn make(resolution: Bits) -> ZResult { let tch = TransportChannelTx { - sn: SeqNumGenerator::make(0, sn_resolution)?, + sn: SeqNumGenerator::make(0, resolution)?, }; Ok(tch) } - pub(crate) fn sync(&mut self, sn: ZInt) -> ZResult<()> { + pub(crate) fn sync(&mut self, sn: TransportSn) -> ZResult<()> { self.sn.set(sn) } } @@ -45,16 +48,16 @@ pub(crate) struct TransportChannelRx { impl TransportChannelRx { pub(crate) fn make( reliability: Reliability, - sn_resolution: ZInt, + resolution: Bits, defrag_buff_size: usize, ) -> ZResult { - let sn = SeqNum::make(0, sn_resolution)?; - let defrag = DefragBuffer::make(reliability, sn_resolution, defrag_buff_size)?; + let sn = SeqNum::make(0, resolution)?; + let defrag = DefragBuffer::make(reliability, resolution, defrag_buff_size)?; let tch = TransportChannelRx { sn, defrag }; Ok(tch) } - pub(crate) fn sync(&mut self, sn: ZInt) -> ZResult<()> { + pub(crate) fn sync(&mut self, sn: TransportSn) -> ZResult<()> { // Set the sequence number in the state as it had received a message with sn - 1 let sn = if sn == 0 { self.sn.resolution() - 1 @@ -68,50 +71,46 @@ impl TransportChannelRx { } #[derive(Clone, Debug)] -pub(crate) struct TransportConduitTx { +pub(crate) struct TransportPriorityTx { pub(crate) reliable: Arc>, pub(crate) best_effort: Arc>, } -impl TransportConduitTx { - pub(crate) fn make(sn_resolution: ZInt) -> ZResult { - let rch = TransportChannelTx::make(sn_resolution)?; - let bch = TransportChannelTx::make(sn_resolution)?; - let ctx = TransportConduitTx { +impl TransportPriorityTx { + pub(crate) fn make(resolution: Bits) -> ZResult { + let rch = TransportChannelTx::make(resolution)?; + let bch = TransportChannelTx::make(resolution)?; + let ctx = TransportPriorityTx { reliable: Arc::new(Mutex::new(rch)), best_effort: Arc::new(Mutex::new(bch)), }; Ok(ctx) } - pub(crate) fn sync(&self, sn: ConduitSn) -> ZResult<()> { + pub(crate) fn sync(&self, sn: PrioritySn) -> ZResult<()> { zlock!(self.reliable).sync(sn.reliable)?; zlock!(self.best_effort).sync(sn.best_effort) } } #[derive(Clone, Debug)] -pub(crate) struct TransportConduitRx { +pub(crate) struct TransportPriorityRx { pub(crate) reliable: Arc>, pub(crate) best_effort: Arc>, } -impl TransportConduitRx { - pub(crate) fn make( - sn_resolution: ZInt, - defrag_buff_size: usize, - ) -> ZResult { - let rch = TransportChannelRx::make(Reliability::Reliable, sn_resolution, defrag_buff_size)?; - let bch = - TransportChannelRx::make(Reliability::BestEffort, sn_resolution, defrag_buff_size)?; - let ctr = TransportConduitRx { +impl TransportPriorityRx { + pub(crate) fn make(resolution: Bits, defrag_buff_size: usize) -> ZResult { + let rch = TransportChannelRx::make(Reliability::Reliable, resolution, defrag_buff_size)?; + let bch = TransportChannelRx::make(Reliability::BestEffort, resolution, defrag_buff_size)?; + let ctr = TransportPriorityRx { reliable: Arc::new(Mutex::new(rch)), best_effort: Arc::new(Mutex::new(bch)), }; Ok(ctr) } - pub(crate) fn sync(&self, sn: ConduitSn) -> ZResult<()> { + pub(crate) fn sync(&self, sn: PrioritySn) -> ZResult<()> { zlock!(self.reliable).sync(sn.reliable)?; zlock!(self.best_effort).sync(sn.best_effort) } diff --git a/io/zenoh-transport/src/common/seq_num.rs b/io/zenoh-transport/src/common/seq_num.rs index 2cbebff2a6..159fd56712 100644 --- a/io/zenoh-transport/src/common/seq_num.rs +++ b/io/zenoh-transport/src/common/seq_num.rs @@ -11,13 +11,27 @@ // Contributors: // ZettaScale Zenoh Team, // -use zenoh_protocol::core::ZInt; +use zenoh_protocol::{core::Bits, transport::TransportSn}; use zenoh_result::{bail, ZResult}; +const RES_U8: TransportSn = (u8::MAX >> 1) as TransportSn; // 1 byte max when encoded +const RES_U16: TransportSn = (u16::MAX >> 2) as TransportSn; // 2 bytes max when encoded +const RES_U32: TransportSn = (u32::MAX >> 4) as TransportSn; // 4 bytes max when encoded +const RES_U64: TransportSn = (u64::MAX >> 1) as TransportSn; // 9 bytes max when encoded + +pub(crate) fn get_mask(resolution: Bits) -> TransportSn { + match resolution { + Bits::U8 => RES_U8, + Bits::U16 => RES_U16, + Bits::U32 => RES_U32, + Bits::U64 => RES_U64, + } +} + /// Sequence Number /// /// Zenoh sequence numbers have a negotiable resolution. Each session can -/// ideally negotiate its resolution and use it across all conduits. +/// ideally negotiate its resolution and use it across all priorities. /// /// The [`SeqNum`][SeqNum] encapsulates the sequence numbers along with a /// the comparison operators that check whether two sequence numbers are @@ -25,9 +39,8 @@ use zenoh_result::{bail, ZResult}; /// #[derive(Clone, Copy, Debug)] pub(crate) struct SeqNum { - value: ZInt, - semi_int: ZInt, - resolution: ZInt, + value: TransportSn, + mask: TransportSn, } impl SeqNum { @@ -47,34 +60,29 @@ impl SeqNum { /// This funtion will panic if `value` is out of bound w.r.t. `resolution`. That is if /// `value` is greater or equal than `resolution`. /// - pub(crate) fn make(value: ZInt, resolution: ZInt) -> ZResult { - let mut sn = SeqNum { - value: 0, - semi_int: resolution >> 1, - resolution, - }; + pub(crate) fn make(value: TransportSn, resolution: Bits) -> ZResult { + let mask = get_mask(resolution); + let mut sn = SeqNum { value: 0, mask }; sn.set(value)?; Ok(sn) } - #[inline(always)] - pub(crate) fn get(&self) -> ZInt { + pub(crate) fn get(&self) -> TransportSn { self.value } #[inline(always)] - pub(crate) fn next(&self) -> ZInt { - (self.value + 1) % self.resolution + pub(crate) fn next(&self) -> TransportSn { + self.value.wrapping_add(1) & self.mask } #[inline(always)] - pub(crate) fn resolution(&self) -> ZInt { - self.resolution + pub(crate) fn resolution(&self) -> TransportSn { + self.mask } - #[inline(always)] - pub(crate) fn set(&mut self, value: ZInt) -> ZResult<()> { - if value >= self.resolution { + pub(crate) fn set(&mut self, value: TransportSn) -> ZResult<()> { + if (value & !self.mask) != 0 { bail!("The sequence number value must be smaller than the resolution"); } @@ -82,7 +90,6 @@ impl SeqNum { Ok(()) } - #[inline(always)] pub(crate) fn increment(&mut self) { self.value = self.next(); } @@ -106,48 +113,21 @@ impl SeqNum { /// # Arguments /// /// * `value` - The sequence number which should be checked for precedence relation. - pub(crate) fn precedes(&self, value: ZInt) -> ZResult { - if value >= self.resolution { + pub(crate) fn precedes(&self, value: TransportSn) -> ZResult { + if (value & !self.mask) != 0 { bail!("The sequence number value must be smaller than the resolution"); } - - let res = if value > self.value { - value - self.value <= self.semi_int - } else { - self.value - value > self.semi_int - }; - - Ok(res) + let gap = value.wrapping_sub(self.value) & self.mask; + Ok((gap != 0) && ((gap & !(self.mask >> 1)) == 0)) } /// Computes the modulo gap between two sequence numbers. - /// - /// Two case are considered: - /// - /// ## Case 1: sna < snb - /// - /// In this case the gap is computed as *snb* - *sna*. - /// - /// ## Case 2: sna > snb - /// - /// In this case the gap is computed as *resolution* - (*sna* - *snb*). - /// - /// # Arguments - /// - /// * `value` - The sequence number which should be checked for gap computation. - #[cfg(test)] // @TODO: remove once reliability is implemented - pub(crate) fn gap(&self, value: ZInt) -> ZResult { - if value >= self.resolution { - bail!("The sequence number value must be smaller than the resolution") + #[cfg(test)] // @TODO: remove #[cfg(test)] once reliability is implemented + pub(crate) fn gap(&self, value: TransportSn) -> ZResult { + if (value & !self.mask) != 0 { + bail!("The sequence number value must be smaller than the resolution"); } - - let gap = if value >= self.value { - value - self.value - } else { - self.resolution - (self.value - value) - }; - - Ok(gap) + Ok(value.wrapping_sub(self.value) & self.mask) } } @@ -171,26 +151,23 @@ impl SeqNumGenerator { /// This funtion will panic if `value` is out of bound w.r.t. `resolution`. That is if /// `value` is greater or equal than `resolution`. /// - pub(crate) fn make(initial_sn: ZInt, sn_resolution: ZInt) -> ZResult { - let sn = SeqNum::make(initial_sn, sn_resolution)?; + pub(crate) fn make(initial_sn: TransportSn, resolution: Bits) -> ZResult { + let sn = SeqNum::make(initial_sn, resolution)?; Ok(SeqNumGenerator(sn)) } - #[inline(always)] - pub(crate) fn now(&mut self) -> ZInt { + pub(crate) fn now(&mut self) -> TransportSn { self.0.get() } /// Generates the next sequence number - #[inline(always)] - pub(crate) fn get(&mut self) -> ZInt { + pub(crate) fn get(&mut self) -> TransportSn { let now = self.now(); self.0.increment(); now } - #[inline(always)] - pub(crate) fn set(&mut self, sn: ZInt) -> ZResult<()> { + pub(crate) fn set(&mut self, sn: TransportSn) -> ZResult<()> { self.0.set(sn) } } @@ -201,17 +178,17 @@ mod tests { #[test] fn sn_set() { - let mut sn0a = SeqNum::make(0, 14).unwrap(); + let mask = (u8::MAX >> 1) as TransportSn; + + let mut sn0a = SeqNum::make(0, Bits::U8).unwrap(); assert_eq!(sn0a.get(), 0); - assert_eq!(sn0a.resolution, 14); + assert_eq!(sn0a.mask, mask); - let res = sn0a.set(13); - assert!(res.is_ok()); - assert_eq!(sn0a.get(), 13); + sn0a.set(mask).unwrap(); + assert_eq!(sn0a.get(), mask); - let res = sn0a.set(14); - assert!(res.is_err()); - assert_eq!(sn0a.get(), 13); + assert!(sn0a.set(mask + 1).is_err()); + assert_eq!(sn0a.get(), mask); sn0a.increment(); assert_eq!(sn0a.get(), 0); @@ -222,76 +199,40 @@ mod tests { #[test] fn sn_gap() { - let mut sn0a = SeqNum::make(0, 14).unwrap(); - let sn1a: ZInt = 0; - let res = sn0a.gap(sn1a); - assert_eq!(res.unwrap(), 0); - - let sn1a: ZInt = 1; - let res = sn0a.gap(sn1a); - assert_eq!(res.unwrap(), 1); + let mask = (u8::MAX >> 1) as TransportSn; + let mut sn0a = SeqNum::make(0, Bits::U8).unwrap(); - let sn1a: ZInt = 13; - let res = sn0a.gap(sn1a); - assert_eq!(res.unwrap(), 13); + assert_eq!(sn0a.gap(0).unwrap(), 0); + assert_eq!(sn0a.gap(1).unwrap(), 1); + assert_eq!(sn0a.gap(mask).unwrap(), mask); + assert!(sn0a.gap(mask + 1).is_err()); - let sn1a: ZInt = 14; - let res = sn0a.gap(sn1a); - assert!(res.is_err()); - - let res = sn0a.set(13); - assert!(res.is_ok()); - - let sn1a: ZInt = 13; - let res = sn0a.gap(sn1a); - assert_eq!(res.unwrap(), 0); - - let sn1a: ZInt = 0; - let res = sn0a.gap(sn1a); - assert_eq!(res.unwrap(), 1); + sn0a.set(mask).unwrap(); + assert_eq!(sn0a.gap(mask).unwrap(), 0); + assert_eq!(sn0a.gap(0).unwrap(), 1); } #[test] fn sn_precedence() { - let mut sn0a = SeqNum::make(0, 14).unwrap(); - let sn1a: ZInt = 1; - let res = sn0a.precedes(sn1a); - assert!(res.unwrap()); - - let sn1a: ZInt = 0; - let res = sn0a.precedes(sn1a); - assert!(!res.unwrap()); - - let sn1a: ZInt = 6; - let res = sn0a.precedes(sn1a); - assert!(res.unwrap()); - - let sn1a: ZInt = 7; - let res = sn0a.precedes(sn1a); - assert!(res.unwrap()); - - let res = sn0a.set(13); - assert!(res.is_ok()); - - let sn1a: ZInt = 6; - let res = sn0a.precedes(sn1a); - assert!(!res.unwrap()); - - let sn1a: ZInt = 1; - let res = sn0a.precedes(sn1a); - assert!(res.unwrap()); + let mask = (u8::MAX >> 1) as TransportSn; - let sn1a: ZInt = 5; - let res = sn0a.precedes(sn1a); - assert!(res.unwrap()); + let sn0a = SeqNum::make(0, Bits::U8).unwrap(); + assert!(sn0a.precedes(1).unwrap()); + assert!(!sn0a.precedes(0).unwrap()); + assert!(!sn0a.precedes(mask).unwrap()); + assert!(sn0a.precedes(6).unwrap()); + assert!(sn0a.precedes((mask / 2) - 1).unwrap()); + assert!(sn0a.precedes(mask / 2).unwrap()); + assert!(!sn0a.precedes((mask / 2) + 1).unwrap()); } #[test] fn sn_generation() { - let mut sn0 = SeqNumGenerator::make(13, 14).unwrap(); - let mut sn1 = SeqNumGenerator::make(5, 14).unwrap(); + let mask = (u8::MAX >> 1) as TransportSn; + let mut sn0 = SeqNumGenerator::make(mask, Bits::U8).unwrap(); + let mut sn1 = SeqNumGenerator::make(5, Bits::U8).unwrap(); - assert_eq!(sn0.get(), 13); + assert_eq!(sn0.get(), mask); assert_eq!(sn1.get(), 5); assert_eq!(sn0.get(), 0); diff --git a/io/zenoh-transport/src/common/stats.rs b/io/zenoh-transport/src/common/stats.rs index e7fb0f5c3e..287018aeb0 100644 --- a/io/zenoh-transport/src/common/stats.rs +++ b/io/zenoh-transport/src/common/stats.rs @@ -22,42 +22,42 @@ macro_rules! stats_struct { } ) => { paste::paste! { - $(#[$meta])* $vis struct $struct_name { $( $(#[$field_meta:meta])* - $field_vis $field_name: usize, + $field_vis $field_name: AtomicUsize, )* } - struct [<$struct_name Atomic>] { + $(#[$meta])* + $vis struct [<$struct_name Report>] { $( $(#[$field_meta:meta])* - $field_name: AtomicUsize, + $field_vis $field_name: usize, )* } - impl [<$struct_name Atomic>] { - fn snapshot(&self) -> $struct_name { - $struct_name { + impl $struct_name { + $vis fn report(&self) -> [<$struct_name Report>] { + [<$struct_name Report>] { $($field_name: self.[](),)* } } $( - fn [](&self) -> usize { + $vis fn [](&self) -> usize { self.$field_name.load(Ordering::Relaxed) } - fn [](&self, nb: usize) { + $vis fn [](&self, nb: usize) { self.$field_name.fetch_add(nb, Ordering::Relaxed); } )* } - impl Default for [<$struct_name Atomic>] { - fn default() -> [<$struct_name Atomic>] { - [<$struct_name Atomic>] { + impl Default for $struct_name { + fn default() -> $struct_name { + $struct_name { $($field_name: AtomicUsize::new(0),)* } } @@ -65,4 +65,47 @@ macro_rules! stats_struct { } } } -pub(crate) use stats_struct; + +use serde::{Deserialize, Serialize}; +use std::sync::atomic::{AtomicUsize, Ordering}; +stats_struct! { + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TransportStats { + pub tx_t_msgs, + pub tx_n_msgs, + pub tx_n_dropped, + pub tx_z_put_user_msgs, + pub tx_z_put_user_pl_bytes, + pub tx_z_put_admin_msgs, + pub tx_z_put_admin_pl_bytes, + pub tx_z_del_user_msgs, + pub tx_z_del_admin_msgs, + pub tx_z_query_user_msgs, + pub tx_z_query_user_pl_bytes, + pub tx_z_query_admin_msgs, + pub tx_z_query_admin_pl_bytes, + pub tx_z_reply_user_msgs, + pub tx_z_reply_user_pl_bytes, + pub tx_z_reply_admin_msgs, + pub tx_z_reply_admin_pl_bytes, + pub tx_bytes, + + pub rx_t_msgs, + pub rx_n_msgs, + pub rx_z_put_user_msgs, + pub rx_z_put_user_pl_bytes, + pub rx_z_put_admin_msgs, + pub rx_z_put_admin_pl_bytes, + pub rx_z_del_user_msgs, + pub rx_z_del_admin_msgs, + pub rx_z_query_user_msgs, + pub rx_z_query_user_pl_bytes, + pub rx_z_query_admin_msgs, + pub rx_z_query_admin_pl_bytes, + pub rx_z_reply_user_msgs, + pub rx_z_reply_user_pl_bytes, + pub rx_z_reply_admin_msgs, + pub rx_z_reply_admin_pl_bytes, + pub rx_bytes, + } +} diff --git a/io/zenoh-transport/src/lib.rs b/io/zenoh-transport/src/lib.rs index bf8a8851e1..05240710f6 100644 --- a/io/zenoh-transport/src/lib.rs +++ b/io/zenoh-transport/src/lib.rs @@ -21,9 +21,13 @@ mod common; mod manager; mod multicast; mod primitives; +pub mod unicast; + +#[cfg(feature = "stats")] +pub use common::stats; + #[cfg(feature = "shared-memory")] mod shm; -pub mod unicast; pub use manager::*; pub use multicast::*; @@ -34,7 +38,7 @@ use std::sync::Arc; pub use unicast::*; use zenoh_link::Link; use zenoh_protocol::core::{WhatAmI, ZenohId}; -use zenoh_protocol::zenoh::ZenohMessage; +use zenoh_protocol::network::NetworkMessage; use zenoh_result::ZResult; /*************************************/ @@ -49,7 +53,7 @@ pub trait TransportEventHandler: Send + Sync { fn new_multicast( &self, - transport: TransportMulticast, + _transport: TransportMulticast, ) -> ZResult>; } @@ -101,19 +105,20 @@ impl TransportMulticastEventHandler for DummyTransportMulticastEventHandler { /*************************************/ /* CALLBACK */ /*************************************/ -#[derive(Clone, Serialize)] +#[derive(Clone, Debug, Serialize, PartialEq, Eq)] #[serde(rename = "Transport")] pub struct TransportPeer { pub zid: ZenohId, pub whatami: WhatAmI, pub is_qos: bool, - pub is_shm: bool, #[serde(skip)] pub links: Vec, + #[cfg(feature = "shared-memory")] + pub is_shm: bool, } pub trait TransportPeerEventHandler: Send + Sync { - fn handle_message(&self, msg: ZenohMessage) -> ZResult<()>; + fn handle_message(&self, msg: NetworkMessage) -> ZResult<()>; fn new_link(&self, src: Link); fn del_link(&self, link: Link); fn closing(&self); @@ -126,7 +131,7 @@ pub trait TransportPeerEventHandler: Send + Sync { pub struct DummyTransportPeerEventHandler; impl TransportPeerEventHandler for DummyTransportPeerEventHandler { - fn handle_message(&self, _message: ZenohMessage) -> ZResult<()> { + fn handle_message(&self, _message: NetworkMessage) -> ZResult<()> { Ok(()) } diff --git a/io/zenoh-transport/src/manager.rs b/io/zenoh-transport/src/manager.rs index cd10461c31..c6a4ed5781 100644 --- a/io/zenoh-transport/src/manager.rs +++ b/io/zenoh-transport/src/manager.rs @@ -11,40 +11,34 @@ // Contributors: // ZettaScale Zenoh Team, // -use super::multicast::manager::{ - TransportManagerBuilderMulticast, TransportManagerConfigMulticast, - TransportManagerStateMulticast, -}; use super::unicast::manager::{ TransportManagerBuilderUnicast, TransportManagerConfigUnicast, TransportManagerStateUnicast, }; -use super::unicast::TransportUnicast; use super::TransportEventHandler; -use async_std::sync::Mutex as AsyncMutex; +use crate::multicast::manager::{ + TransportManagerBuilderMulticast, TransportManagerConfigMulticast, + TransportManagerStateMulticast, +}; +use async_std::{sync::Mutex as AsyncMutex, task}; use rand::{RngCore, SeedableRng}; use std::collections::HashMap; use std::sync::Arc; -#[cfg(feature = "shared-memory")] -use std::sync::RwLock; use std::time::Duration; -use zenoh_cfg_properties::{config::*, Properties}; -use zenoh_config::{Config, QueueConf, QueueSizeConf}; -use zenoh_core::zparse; +use zenoh_config::{Config, LinkRxConf, QueueConf, QueueSizeConf}; use zenoh_crypto::{BlockCipher, PseudoRng}; use zenoh_link::NewLinkChannelSender; use zenoh_protocol::{ - core::{EndPoint, Locator, Priority, WhatAmI, ZInt, ZenohId}, - defaults::{BATCH_SIZE, SEQ_NUM_RES, VERSION}, + core::{EndPoint, Field, Locator, Priority, Resolution, WhatAmI, ZenohId}, + transport::BatchSize, + VERSION, }; use zenoh_result::{bail, ZResult}; -#[cfg(feature = "shared-memory")] -use zenoh_shm::SharedMemoryReader; /// # Examples /// ``` /// use std::sync::Arc; /// use std::time::Duration; -/// use zenoh_protocol::core::{ZenohId, WhatAmI, whatami}; +/// use zenoh_protocol::core::{ZenohId, Resolution, Field, Bits, WhatAmI, whatami}; /// use zenoh_transport::*; /// use zenoh_result::ZResult; /// @@ -60,10 +54,11 @@ use zenoh_shm::SharedMemoryReader; /// Ok(Arc::new(DummyTransportPeerEventHandler)) /// } /// -/// fn new_multicast(&self, -/// _transport: TransportMulticast +/// fn new_multicast( +/// &self, +/// _transport: TransportMulticast, /// ) -> ZResult> { -/// Ok(Arc::new(DummyTransportMulticastEventHandler::default())) +/// Ok(Arc::new(DummyTransportMulticastEventHandler)) /// } /// } /// @@ -79,13 +74,14 @@ use zenoh_shm::SharedMemoryReader; /// .keep_alive(4) // Send a KeepAlive every 250 ms /// .accept_timeout(Duration::from_secs(1)) /// .accept_pending(10) // Set to 10 the number of simultanous pending incoming transports -/// .max_links(1) // Allow max 1 inbound link per transport /// .max_sessions(5); // Allow max 5 transports open +/// let mut resolution = Resolution::default(); +/// resolution.set(Field::FrameSN, Bits::U8); /// let manager = TransportManager::builder() /// .zid(ZenohId::rand()) /// .whatami(WhatAmI::Peer) /// .batch_size(1_024) // Use a batch size of 1024 bytes -/// .sn_resolution(128) // Use a sequence number resolution of 128 +/// .resolution(resolution) // Use a sequence number resolution of 128 /// .unicast(unicast) // Configure unicast parameters /// .build(Arc::new(MySH::default())) /// .unwrap(); @@ -95,7 +91,7 @@ pub struct TransportManagerConfig { pub version: u8, pub zid: ZenohId, pub whatami: WhatAmI, - pub sn_resolution: ZInt, + pub resolution: Resolution, pub batch_size: u16, pub queue_size: [usize; Priority::NUM], pub queue_backoff: Duration, @@ -103,7 +99,7 @@ pub struct TransportManagerConfig { pub link_rx_buffer_size: usize, pub unicast: TransportManagerConfigUnicast, pub multicast: TransportManagerConfigMulticast, - pub endpoint: HashMap, + pub endpoints: HashMap, // (protocol, config) pub handler: Arc, pub tx_threads: usize, pub protocols: Vec, @@ -123,7 +119,7 @@ pub struct TransportManagerBuilder { version: u8, zid: ZenohId, whatami: WhatAmI, - sn_resolution: ZInt, + resolution: Resolution, batch_size: u16, queue_size: QueueSizeConf, queue_backoff: Duration, @@ -131,7 +127,7 @@ pub struct TransportManagerBuilder { link_rx_buffer_size: usize, unicast: TransportManagerBuilderUnicast, multicast: TransportManagerBuilderMulticast, - endpoint: HashMap, + endpoints: HashMap, // (protocol, config) tx_threads: usize, protocols: Option>, } @@ -147,8 +143,8 @@ impl TransportManagerBuilder { self } - pub fn sn_resolution(mut self, sn_resolution: ZInt) -> Self { - self.sn_resolution = sn_resolution; + pub fn resolution(mut self, resolution: Resolution) -> Self { + self.resolution = resolution; self } @@ -177,8 +173,8 @@ impl TransportManagerBuilder { self } - pub fn endpoint(mut self, endpoint: HashMap) -> Self { - self.endpoint = endpoint; + pub fn endpoints(mut self, endpoints: HashMap) -> Self { + self.endpoints = endpoints; self } @@ -208,20 +204,16 @@ impl TransportManagerBuilder { self = self.whatami(*v); } - self = self.sn_resolution( - config - .transport() - .link() - .tx() - .sequence_number_resolution() - .unwrap(), - ); - self = self.batch_size(config.transport().link().tx().batch_size().unwrap()); - self = self.defrag_buff_size(config.transport().link().rx().max_message_size().unwrap()); - self = self.link_rx_buffer_size(config.transport().link().rx().buffer_size().unwrap()); - self = self.queue_size(config.transport().link().tx().queue().size().clone()); - self = self.tx_threads(config.transport().link().tx().threads().unwrap()); - self = self.protocols(config.transport().link().protocols().clone()); + let link = config.transport().link(); + let mut resolution = Resolution::default(); + resolution.set(Field::FrameSN, *link.tx().sequence_number_resolution()); + self = self.resolution(resolution); + self = self.batch_size(*link.tx().batch_size()); + self = self.defrag_buff_size(*link.rx().max_message_size()); + self = self.link_rx_buffer_size(*link.rx().buffer_size()); + self = self.queue_size(link.tx().queue().size().clone()); + self = self.tx_threads(*link.tx().threads()); + self = self.protocols(link.protocols().clone()); let (c, errors) = zenoh_link::LinkConfigurator::default() .configurations(config) @@ -234,7 +226,7 @@ impl TransportManagerBuilder { } bail!("{}", formatter); } - self = self.endpoint(c); + self = self.endpoints(c); self = self.unicast( TransportManagerBuilderUnicast::default() .from_config(config) @@ -250,7 +242,10 @@ impl TransportManagerBuilder { } pub fn build(self, handler: Arc) -> ZResult { - let unicast = self.unicast.build()?; + // Initialize the PRNG and the Cipher + let mut prng = PseudoRng::from_entropy(); + + let unicast = self.unicast.build(&mut prng)?; let multicast = self.multicast.build()?; let mut queue_size = [0; Priority::NUM]; @@ -267,7 +262,7 @@ impl TransportManagerBuilder { version: self.version, zid: self.zid, whatami: self.whatami, - sn_resolution: self.sn_resolution, + resolution: self.resolution, batch_size: self.batch_size, queue_size, queue_backoff: self.queue_backoff, @@ -275,7 +270,7 @@ impl TransportManagerBuilder { link_rx_buffer_size: self.link_rx_buffer_size, unicast: unicast.config, multicast: multicast.config, - endpoint: self.endpoint, + endpoints: self.endpoints, handler, tx_threads: self.tx_threads, protocols: self.protocols.unwrap_or_else(|| { @@ -293,25 +288,26 @@ impl TransportManagerBuilder { let params = TransportManagerParams { config, state }; - Ok(TransportManager::new(params)) + Ok(TransportManager::new(params, prng)) } } impl Default for TransportManagerBuilder { fn default() -> Self { + let link_rx = LinkRxConf::default(); let queue = QueueConf::default(); - let backoff = queue.backoff().unwrap(); + let backoff = *queue.backoff(); Self { version: VERSION, zid: ZenohId::rand(), - whatami: ZN_MODE_DEFAULT.parse().unwrap(), - sn_resolution: SEQ_NUM_RES, - batch_size: BATCH_SIZE, + whatami: zenoh_config::defaults::mode, + resolution: Resolution::default(), + batch_size: BatchSize::MAX, queue_size: queue.size, queue_backoff: Duration::from_nanos(backoff), - defrag_buff_size: zparse!(ZN_DEFRAG_BUFF_SIZE_DEFAULT).unwrap(), - link_rx_buffer_size: zparse!(ZN_LINK_RX_BUFF_SIZE_DEFAULT).unwrap(), - endpoint: HashMap::new(), + defrag_buff_size: *link_rx.max_message_size(), + link_rx_buffer_size: *link_rx.buffer_size(), + endpoints: HashMap::new(), unicast: TransportManagerBuilderUnicast::default(), multicast: TransportManagerBuilderMulticast::default(), tx_threads: 1, @@ -359,17 +355,14 @@ pub struct TransportManager { pub(crate) state: Arc, pub(crate) prng: Arc>, pub(crate) cipher: Arc, - #[cfg(feature = "shared-memory")] - pub(crate) shmr: Arc>, pub(crate) locator_inspector: zenoh_link::LocatorInspector, pub(crate) new_unicast_link_sender: NewLinkChannelSender, pub(crate) tx_executor: TransportExecutor, } impl TransportManager { - pub fn new(params: TransportManagerParams) -> TransportManager { - // Initialize the PRNG and the Cipher - let mut prng = PseudoRng::from_entropy(); + pub fn new(params: TransportManagerParams, mut prng: PseudoRng) -> TransportManager { + // Initialize the Cipher let mut key = [0_u8; BlockCipher::BLOCK_SIZE]; prng.fill_bytes(&mut key); let cipher = BlockCipher::new(key); @@ -383,8 +376,6 @@ impl TransportManager { state: Arc::new(params.state), prng: Arc::new(AsyncMutex::new(prng)), cipher: Arc::new(cipher), - #[cfg(feature = "shared-memory")] - shmr: Arc::new(RwLock::new(SharedMemoryReader::new())), locator_inspector: Default::default(), new_unicast_link_sender, tx_executor: TransportExecutor::new(tx_threads), @@ -412,9 +403,7 @@ impl TransportManager { } pub async fn close(&self) { - log::trace!("TransportManager::clear())"); self.close_unicast().await; - self.close_multicast().await; self.tx_executor.stop().await; } @@ -422,27 +411,12 @@ impl TransportManager { /* LISTENER */ /*************************************/ pub async fn add_listener(&self, endpoint: EndPoint) -> ZResult { - let p = endpoint.protocol(); - if !self - .config - .protocols - .iter() - .any(|x| x.as_str() == p.as_str()) - { - bail!( - "Unsupported protocol: {}. Supported protocols are: {:?}", - p, - self.config.protocols - ); - } - if self .locator_inspector .is_multicast(&endpoint.to_locator()) .await? { - // @TODO: multicast - bail!("Unimplemented"); + self.add_listener_multicast(endpoint).await } else { self.add_listener_unicast(endpoint).await } @@ -454,51 +428,23 @@ impl TransportManager { .is_multicast(&endpoint.to_locator()) .await? { - // @TODO: multicast - bail!("Unimplemented"); + self.del_listener_multicast(endpoint).await } else { self.del_listener_unicast(endpoint).await } } pub fn get_listeners(&self) -> Vec { - self.get_listeners_unicast() - // @TODO: multicast + let mut lsu = task::block_on(self.get_listeners_unicast()); + let mut lsm = task::block_on(self.get_listeners_multicast()); + lsu.append(&mut lsm); + lsu } pub fn get_locators(&self) -> Vec { - self.get_locators_unicast() - // @TODO: multicast - } - - /*************************************/ - /* TRANSPORT */ - /*************************************/ - pub fn get_transport(&self, peer: &ZenohId) -> Option { - self.get_transport_unicast(peer) - // @TODO: multicast - } - - pub fn get_transports(&self) -> Vec { - self.get_transports_unicast() - // @TODO: multicast - } - - pub async fn open_transport(&self, endpoint: EndPoint) -> ZResult { - let p = endpoint.protocol(); - if !self - .config - .protocols - .iter() - .any(|x| x.as_str() == p.as_str()) - { - bail!( - "Unsupported protocol: {}. Supported protocols are: {:?}", - p, - self.config.protocols - ); - } - - self.open_transport_unicast(endpoint).await + let mut lsu = task::block_on(self.get_locators_unicast()); + let mut lsm = task::block_on(self.get_locators_multicast()); + lsu.append(&mut lsm); + lsu } } diff --git a/io/zenoh-transport/src/multicast/establishment.rs b/io/zenoh-transport/src/multicast/establishment.rs index cd6157acc9..fc4ad21da3 100644 --- a/io/zenoh-transport/src/multicast/establishment.rs +++ b/io/zenoh-transport/src/multicast/establishment.rs @@ -11,14 +11,20 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::multicast::{TransportMulticast, TransportMulticastConfig, TransportMulticastInner}; -use crate::TransportManager; +use crate::{ + common::seq_num, + multicast::{transport::TransportMulticastInner, TransportMulticast}, + TransportConfigMulticast, TransportManager, +}; use rand::Rng; use std::sync::Arc; -use zenoh_core::{zasynclock, zlock}; +use zenoh_core::zasynclock; use zenoh_link::LinkMulticast; -use zenoh_protocol::core::{ConduitSn, ConduitSnList, Priority}; -use zenoh_result::ZResult; +use zenoh_protocol::{ + core::{Field, Priority}, + transport::PrioritySn, +}; +use zenoh_result::{bail, ZResult}; pub(crate) async fn open_link( manager: &TransportManager, @@ -27,58 +33,67 @@ pub(crate) async fn open_link( // Create and configure the multicast transport let mut prng = zasynclock!(manager.prng); - macro_rules! zgen_conduit_sn { + // Generate initial SNs + let sn_resolution = manager.config.resolution.get(Field::FrameSN); + let max = seq_num::get_mask(sn_resolution); + macro_rules! zgen_prioritysn { () => { - ConduitSn { - reliable: prng.gen_range(0..manager.config.sn_resolution), - best_effort: prng.gen_range(0..manager.config.sn_resolution), + PrioritySn { + reliable: prng.gen_range(0..=max), + best_effort: prng.gen_range(0..=max), } }; } - let locator = link.get_dst(); let initial_sns = if manager.config.multicast.is_qos { - let mut initial_sns = [ConduitSn::default(); Priority::NUM]; - for isn in initial_sns.iter_mut() { - *isn = zgen_conduit_sn!(); - } - ConduitSnList::QoS(initial_sns.into()) + (0..Priority::NUM).map(|_| zgen_prioritysn!()).collect() } else { - ConduitSnList::Plain(zgen_conduit_sn!()) - }; - let config = TransportMulticastConfig { - manager: manager.clone(), + vec![zgen_prioritysn!()] + } + .into_boxed_slice(); + + // Release the lock + drop(prng); + + // Create the transport + let locator = link.get_dst().to_owned(); + let config = TransportConfigMulticast { + link, + sn_resolution, initial_sns, - link: link.clone(), + #[cfg(feature = "shared-memory")] + is_shm: manager.config.multicast.is_shm, }; - let ti = Arc::new(TransportMulticastInner::make(config)?); + let ti = Arc::new(TransportMulticastInner::make(manager.clone(), config)?); + + // Add the link + ti.start_tx()?; // Store the active transport - let transport: TransportMulticast = (&ti).into(); - zlock!(manager.state.multicast.transports).insert(locator.to_owned(), ti.clone()); + let mut w_guard = zasynclock!(manager.state.multicast.transports); + if w_guard.get(&locator).is_some() { + bail!("A Multicast transport on {} already exist!", locator); + } + w_guard.insert(locator.clone(), ti.clone()); + drop(w_guard); // Notify the transport event handler - let batch_size = manager.config.batch_size.min(link.get_mtu()); - ti.start_tx(batch_size).map_err(|e| { - zlock!(manager.state.multicast.transports).remove(locator); - let _ = ti.stop_tx(); - e - })?; - let callback = manager - .config - .handler - .new_multicast(transport.clone()) - .map_err(|e| { - zlock!(manager.state.multicast.transports).remove(locator); + let transport: TransportMulticast = (&ti).into(); + + let callback = match manager.config.handler.new_multicast(transport.clone()) { + Ok(c) => c, + Err(e) => { + zasynclock!(manager.state.multicast.transports).remove(&locator); let _ = ti.stop_tx(); - e - })?; + return Err(e); + } + }; ti.set_callback(callback); - ti.start_rx().map_err(|e| { - zlock!(manager.state.multicast.transports).remove(locator); + if let Err(e) = ti.start_rx() { + zasynclock!(manager.state.multicast.transports).remove(&locator); let _ = ti.stop_rx(); - e - })?; + return Err(e); + } Ok(transport) } diff --git a/io/zenoh-transport/src/multicast/link.rs b/io/zenoh-transport/src/multicast/link.rs index 6f8aca99b3..95d3b90036 100644 --- a/io/zenoh-transport/src/multicast/link.rs +++ b/io/zenoh-transport/src/multicast/link.rs @@ -11,31 +11,29 @@ // Contributors: // ZettaScale Zenoh Team, // -use super::common::{conduit::TransportConduitTx, pipeline::TransmissionPipeline}; +use super::common::{pipeline::TransmissionPipeline, priority::TransportPriorityTx}; use super::transport::TransportMulticastInner; -#[cfg(feature = "stats")] -use super::TransportMulticastStatsAtomic; use crate::common::batch::WBatch; use crate::common::pipeline::{ TransmissionPipelineConf, TransmissionPipelineConsumer, TransmissionPipelineProducer, }; +#[cfg(feature = "stats")] +use crate::stats::TransportStats; use async_std::prelude::FutureExt; use async_std::task; use async_std::task::JoinHandle; use std::convert::TryInto; use std::sync::Arc; use std::time::{Duration, Instant}; -use zenoh_buffers::reader::{HasReader, Reader}; -use zenoh_codec::{RCodec, Zenoh060}; +use zenoh_buffers::ZSlice; use zenoh_core::zlock; use zenoh_link::{LinkMulticast, Locator}; use zenoh_protocol::{ - core::{ConduitSn, ConduitSnList, Priority, WhatAmI, ZInt, ZenohId}, - transport::TransportMessage, + core::{Bits, Priority, Resolution, WhatAmI, ZenohId}, + transport::{BatchSize, Join, KeepAlive, PrioritySn, TransportMessage, TransportSn}, }; use zenoh_result::{bail, zerror, ZResult}; -use zenoh_sync::RecyclingObjectPool; -use zenoh_sync::Signal; +use zenoh_sync::{RecyclingObjectPool, Signal}; pub(super) struct TransportLinkMulticastConfig { pub(super) version: u8, @@ -44,8 +42,8 @@ pub(super) struct TransportLinkMulticastConfig { pub(super) lease: Duration, pub(super) keep_alive: usize, pub(super) join_interval: Duration, - pub(super) sn_resolution: ZInt, - pub(super) batch_size: u16, + pub(super) sn_resolution: Bits, + pub(super) batch_size: BatchSize, } #[derive(Clone)] @@ -82,15 +80,15 @@ impl TransportLinkMulticast { pub(super) fn start_tx( &mut self, config: TransportLinkMulticastConfig, - conduit_tx: Arc<[TransportConduitTx]>, + priority_tx: Arc<[TransportPriorityTx]>, ) { - let initial_sns: Vec = conduit_tx + let initial_sns: Vec = priority_tx .iter() - .map(|x| ConduitSn { + .map(|x| PrioritySn { reliable: { let sn = zlock!(x.reliable).sn.now(); if sn == 0 { - config.sn_resolution - 1 + config.sn_resolution.mask() as TransportSn } else { sn - 1 } @@ -98,7 +96,7 @@ impl TransportLinkMulticast { best_effort: { let sn = zlock!(x.best_effort).sn.now(); if sn == 0 { - config.sn_resolution - 1 + config.sn_resolution.mask() as TransportSn } else { sn - 1 } @@ -109,17 +107,17 @@ impl TransportLinkMulticast { if self.handle_tx.is_none() { let tpc = TransmissionPipelineConf { is_streamed: false, - batch_size: config.batch_size.min(self.link.get_mtu()), + batch_size: config.batch_size, queue_size: self.transport.manager.config.queue_size, backoff: self.transport.manager.config.queue_backoff, }; // The pipeline - let (producer, consumer) = TransmissionPipeline::make(tpc, &conduit_tx); + let (producer, consumer) = TransmissionPipeline::make(tpc, &priority_tx); self.pipeline = Some(producer); // Spawn the TX task let c_link = self.link.clone(); - let c_transport = self.transport.clone(); + let ctransport = self.transport.clone(); let handle = task::spawn(async move { let res = tx_task( consumer, @@ -127,14 +125,14 @@ impl TransportLinkMulticast { config, initial_sns, #[cfg(feature = "stats")] - c_transport.stats.clone(), + ctransport.stats.clone(), ) .await; if let Err(e) = res { log::debug!("{}", e); // Spawn a task to avoid a deadlock waiting for this same task // to finish in the close() joining its handle - task::spawn(async move { c_transport.delete().await }); + task::spawn(async move { ctransport.delete().await }); } }); self.handle_tx = Some(Arc::new(handle)); @@ -147,11 +145,11 @@ impl TransportLinkMulticast { } } - pub(super) fn start_rx(&mut self) { + pub(super) fn start_rx(&mut self, batch_size: BatchSize) { if self.handle_rx.is_none() { // Spawn the RX task let c_link = self.link.clone(); - let c_transport = self.transport.clone(); + let ctransport = self.transport.clone(); let c_signal = self.signal_rx.clone(); let c_rx_buffer_size = self.transport.manager.config.link_rx_buffer_size; @@ -159,9 +157,10 @@ impl TransportLinkMulticast { // Start the consume task let res = rx_task( c_link.clone(), - c_transport.clone(), + ctransport.clone(), c_signal.clone(), c_rx_buffer_size, + batch_size, ) .await; c_signal.trigger(); @@ -169,7 +168,7 @@ impl TransportLinkMulticast { log::debug!("{}", e); // Spawn a task to avoid a deadlock waiting for this same task // to finish in the close() joining its handle - task::spawn(async move { c_transport.delete().await }); + task::spawn(async move { ctransport.delete().await }); } }); self.handle_rx = Some(Arc::new(handle)); @@ -207,8 +206,8 @@ async fn tx_task( mut pipeline: TransmissionPipelineConsumer, link: LinkMulticast, config: TransportLinkMulticastConfig, - mut last_sns: Vec, - #[cfg(feature = "stats")] stats: Arc, + mut last_sns: Vec, + #[cfg(feature = "stats")] stats: Arc, ) -> ZResult<()> { enum Action { Pull((WBatch, usize)), @@ -264,33 +263,35 @@ async fn tx_task( pipeline.refill(batch, priority); } Action::Join => { - let attachment = None; let next_sns = last_sns .iter() - .map(|c| ConduitSn { - reliable: (1 + c.reliable) % config.sn_resolution, - best_effort: (1 + c.best_effort) % config.sn_resolution, + .map(|c| PrioritySn { + reliable: (1 + c.reliable) & config.sn_resolution.mask() as TransportSn, + best_effort: (1 + c.best_effort) + & config.sn_resolution.mask() as TransportSn, }) - .collect::>(); - let next_sns = if next_sns.len() == Priority::NUM { - let tmp: [ConduitSn; Priority::NUM] = next_sns.try_into().unwrap(); - ConduitSnList::QoS(tmp.into()) + .collect::>(); + let (next_sn, ext_qos) = if next_sns.len() == Priority::NUM { + let tmp: [PrioritySn; Priority::NUM] = next_sns.try_into().unwrap(); + (PrioritySn::default(), Some(Box::new(tmp))) } else { - assert_eq!(next_sns.len(), 1); - ConduitSnList::Plain(next_sns[0]) + (next_sns[0], None) }; - let message = TransportMessage::make_join( - config.version, - config.whatami, - config.zid, - config.lease, - config.sn_resolution, - next_sns, - attachment, - ); + let message: TransportMessage = Join { + version: config.version, + whatami: config.whatami, + zid: config.zid, + resolution: Resolution::default(), + batch_size: config.batch_size, + lease: config.lease, + next_sn, + ext_qos, + ext_shm: None, + } + .into(); #[allow(unused_variables)] // Used when stats feature is enabled - let n = link.write_transport_message(&message).await?; + let n = link.send(&message).await?; #[cfg(feature = "stats")] { stats.inc_tx_t_msgs(1); @@ -300,12 +301,10 @@ async fn tx_task( last_join = Instant::now(); } Action::KeepAlive => { - let zid = Some(config.zid); - let attachment = None; - let message = TransportMessage::make_keep_alive(zid, attachment); + let message: TransportMessage = KeepAlive.into(); #[allow(unused_variables)] // Used when stats feature is enabled - let n = link.write_transport_message(&message).await?; + let n = link.send(&message).await?; #[cfg(feature = "stats")] { stats.inc_tx_t_msgs(1); @@ -346,6 +345,7 @@ async fn rx_task( transport: TransportMulticastInner, signal: Signal, rx_buffer_size: usize, + batch_size: BatchSize, ) -> ZResult<()> { enum Action { Read((usize, Locator)), @@ -362,9 +362,6 @@ async fn rx_task( Ok(Action::Stop) } - // The codec - let codec = Zenoh060::default(); - // The pool of buffers let mtu = link.get_mtu() as usize; let mut n = rx_buffer_size / mtu; @@ -388,17 +385,16 @@ async fn rx_task( transport.stats.inc_rx_bytes(n); // Deserialize all the messages from the current ZBuf - let mut reader = buffer[0..n].reader(); - while reader.can_read() { - let msg: TransportMessage = codec - .read(&mut reader) - .map_err(|_| zerror!("{}: decoding error", link))?; - + let zslice = ZSlice::make(Arc::new(buffer), 0, n) + .map_err(|_| zerror!("Read {} bytes but buffer is {} bytes", n, mtu))?; + transport.read_messages( + zslice, + &link, + batch_size, + &loc, #[cfg(feature = "stats")] - transport.stats.inc_rx_t_msgs(1); - - transport.receive_message(msg, &loc)? - } + &transport, + )?; } Action::Stop => break, } diff --git a/io/zenoh-transport/src/multicast/manager.rs b/io/zenoh-transport/src/multicast/manager.rs index 8970927659..5d996d25ad 100644 --- a/io/zenoh-transport/src/multicast/manager.rs +++ b/io/zenoh-transport/src/multicast/manager.rs @@ -11,16 +11,21 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::multicast::transport::TransportMulticastInner; -use crate::multicast::TransportMulticast; +#[cfg(feature = "shared-memory")] +use crate::multicast::shm::SharedMemoryMulticast; +use crate::multicast::{transport::TransportMulticastInner, TransportMulticast}; use crate::TransportManager; +use async_std::sync::Mutex; use std::collections::HashMap; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use std::time::Duration; -use zenoh_config::{Config, ZN_LINK_KEEP_ALIVE_DEFAULT, ZN_LINK_LEASE_DEFAULT}; -use zenoh_core::{zlock, zparse}; +#[cfg(feature = "shared-memory")] +use zenoh_config::SharedMemoryConf; +use zenoh_config::{Config, LinkTxConf}; +use zenoh_core::zasynclock; use zenoh_link::*; -use zenoh_protocol::{core::endpoint::Protocol, transport::tmsg}; +use zenoh_protocol::core::ZenohId; +use zenoh_protocol::{core::endpoint, transport::close}; use zenoh_result::{bail, zerror, ZResult}; pub struct TransportManagerConfigMulticast { @@ -29,6 +34,8 @@ pub struct TransportManagerConfigMulticast { pub join_interval: Duration, pub max_sessions: usize, pub is_qos: bool, + #[cfg(feature = "shared-memory")] + pub is_shm: bool, } pub struct TransportManagerBuilderMulticast { @@ -37,6 +44,8 @@ pub struct TransportManagerBuilderMulticast { join_interval: Duration, max_sessions: usize, is_qos: bool, + #[cfg(feature = "shared-memory")] + is_shm: bool, } pub struct TransportManagerStateMulticast { @@ -44,6 +53,9 @@ pub struct TransportManagerStateMulticast { pub(crate) protocols: Arc>>, // Established transports pub(crate) transports: Arc>>>, + // Shared memory + #[cfg(feature = "shared-memory")] + pub(super) shm: Arc, } pub struct TransportManagerParamsMulticast { @@ -77,21 +89,31 @@ impl TransportManagerBuilderMulticast { self } + #[cfg(feature = "shared-memory")] + pub fn shm(mut self, is_shm: bool) -> Self { + self.is_shm = is_shm; + self + } + pub async fn from_config( mut self, config: &Config, ) -> ZResult { self = self.lease(Duration::from_millis( - config.transport().link().tx().lease().unwrap(), + *config.transport().link().tx().lease(), )); - self = self.keep_alive(config.transport().link().tx().keep_alive().unwrap()); + self = self.keep_alive(*config.transport().link().tx().keep_alive()); self = self.join_interval(Duration::from_millis( config.transport().multicast().join_interval().unwrap(), )); self = self.max_sessions(config.transport().multicast().max_sessions().unwrap()); - // Force QoS deactivation in multicast - // self = self.qos(*config.transport().qos().enabled()); @TODO + // @TODO: Force QoS deactivation in multicast since it is not supported + // self = self.qos(*config.transport().qos().enabled()); self = self.qos(false); + #[cfg(feature = "shared-memory")] + { + self = self.shm(*config.transport().shared_memory().enabled()); + } Ok(self) } @@ -103,11 +125,15 @@ impl TransportManagerBuilderMulticast { join_interval: self.join_interval, max_sessions: self.max_sessions, is_qos: self.is_qos, + #[cfg(feature = "shared-memory")] + is_shm: self.is_shm, }; let state = TransportManagerStateMulticast { protocols: Arc::new(Mutex::new(HashMap::new())), transports: Arc::new(Mutex::new(HashMap::new())), + #[cfg(feature = "shared-memory")] + shm: Arc::new(SharedMemoryMulticast::make()?), }; let params = TransportManagerParamsMulticast { config, state }; @@ -118,12 +144,18 @@ impl TransportManagerBuilderMulticast { impl Default for TransportManagerBuilderMulticast { fn default() -> TransportManagerBuilderMulticast { + let link_tx = LinkTxConf::default(); + #[cfg(feature = "shared-memory")] + let shm = SharedMemoryConf::default(); + let tmb = TransportManagerBuilderMulticast { - lease: Duration::from_millis(zparse!(ZN_LINK_LEASE_DEFAULT).unwrap()), - keep_alive: zparse!(ZN_LINK_KEEP_ALIVE_DEFAULT).unwrap(), + lease: Duration::from_millis(*link_tx.lease()), + keep_alive: *link_tx.keep_alive(), join_interval: Duration::from_millis(0), max_sessions: 0, is_qos: false, + #[cfg(feature = "shared-memory")] + is_shm: *shm.enabled(), }; async_std::task::block_on(tmb.from_config(&Config::default())).unwrap() } @@ -137,34 +169,38 @@ impl TransportManager { pub async fn close_multicast(&self) { log::trace!("TransportManagerMulticast::clear())"); - zlock!(self.state.multicast.protocols).clear(); + zasynclock!(self.state.multicast.protocols).clear(); - let mut tm_guard = zlock!(self.state.multicast.transports) - .drain() - .map(|(_, v)| v) - .collect::>>(); - for tm in tm_guard.drain(..) { - let _ = tm.close(tmsg::close_reason::GENERIC).await; + for (_, tm) in zasynclock!(self.state.multicast.transports).drain() { + let _ = tm.close(close::reason::GENERIC).await; } } /*************************************/ /* LINK MANAGER */ /*************************************/ - fn new_link_manager_multicast(&self, protocol: &Protocol) -> ZResult { - let mut w_guard = zlock!(self.state.multicast.protocols); - match w_guard.get(protocol.as_str()) { + async fn new_link_manager_multicast(&self, protocol: &str) -> ZResult { + if !self.config.protocols.iter().any(|x| x.as_str() == protocol) { + bail!( + "Unsupported protocol: {}. Supported protocols are: {:?}", + protocol, + self.config.protocols + ); + } + + let mut w_guard = zasynclock!(self.state.multicast.protocols); + match w_guard.get(protocol) { Some(lm) => Ok(lm.clone()), None => { - let lm = LinkManagerBuilderMulticast::make(protocol.as_str())?; + let lm = LinkManagerBuilderMulticast::make(protocol)?; w_guard.insert(protocol.to_string(), lm.clone()); Ok(lm) } } } - fn del_link_manager_multicast(&self, protocol: &Protocol) -> ZResult<()> { - match zlock!(self.state.multicast.protocols).remove(protocol.as_str()) { + async fn del_link_manager_multicast(&self, protocol: &str) -> ZResult<()> { + match zasynclock!(self.state.multicast.protocols).remove(protocol) { Some(_) => Ok(()), None => bail!( "Can not delete the link manager for protocol ({}) because it has not been found.", @@ -199,16 +235,20 @@ impl TransportManager { .await? { bail!( - "Can not open a multicast transport with a unicast unicast: {}.", + "Can not open a multicast transport with a unicast endpoint: {}.", endpoint ) } // Automatically create a new link manager for the protocol if it does not exist - let manager = self.new_link_manager_multicast(&endpoint.protocol())?; + let manager = self + .new_link_manager_multicast(endpoint.protocol().as_str()) + .await?; // Fill and merge the endpoint configuration - if let Some(config) = self.config.endpoint.get(endpoint.protocol().as_str()) { - endpoint.config_mut().extend(config.iter())?; + if let Some(config) = self.config.endpoints.get(endpoint.protocol().as_str()) { + endpoint + .config_mut() + .extend(endpoint::Parameters::iter(config))?; } // Open the link @@ -216,26 +256,64 @@ impl TransportManager { super::establishment::open_link(self, link).await } - pub fn get_transport_multicast(&self, locator: &Locator) -> Option { - zlock!(self.state.multicast.transports) - .get(locator) - .map(|t| t.into()) + pub async fn get_transport_multicast(&self, zid: &ZenohId) -> Option { + for t in zasynclock!(self.state.multicast.transports).values() { + if t.get_peers().iter().any(|p| p.zid == *zid) { + return Some(t.into()); + } + } + None } - pub fn get_transports_multicast(&self) -> Vec { - zlock!(self.state.multicast.transports) + pub async fn get_transports_multicast(&self) -> Vec { + zasynclock!(self.state.multicast.transports) .values() .map(|t| t.into()) .collect() } - pub(super) fn del_transport_multicast(&self, locator: &Locator) -> ZResult<()> { - let mut guard = zlock!(self.state.multicast.transports); + pub(super) async fn del_transport_multicast(&self, locator: &Locator) -> ZResult<()> { + let mut guard = zasynclock!(self.state.multicast.transports); let res = guard.remove(locator); - let proto = locator.protocol(); - if !guard.iter().any(|(l, _)| l.protocol() == proto) { - let _ = self.del_link_manager_multicast(&proto); + if !guard + .iter() + .any(|(l, _)| l.protocol() == locator.protocol()) + { + let _ = self + .del_link_manager_multicast(locator.protocol().as_str()) + .await; + } + + res.map(|_| ()).ok_or_else(|| { + let e = zerror!("Can not delete the transport for locator: {}", locator); + log::trace!("{}", e); + e.into() + }) + } + + /*************************************/ + /* LISTENER */ + /*************************************/ + pub async fn add_listener_multicast(&self, endpoint: EndPoint) -> ZResult { + let locator = endpoint.to_locator().to_owned(); + self.open_transport_multicast(endpoint).await?; + Ok(locator) + } + + pub async fn del_listener_multicast(&self, endpoint: &EndPoint) -> ZResult<()> { + let locator = endpoint.to_locator(); + + let mut guard = zasynclock!(self.state.multicast.transports); + let res = guard.remove(&locator); + + if !guard + .iter() + .any(|(l, _)| l.protocol() == locator.protocol()) + { + let _ = self + .del_link_manager_multicast(locator.protocol().as_str()) + .await; } res.map(|_| ()).ok_or_else(|| { @@ -244,4 +322,18 @@ impl TransportManager { e.into() }) } + + pub async fn get_listeners_multicast(&self) -> Vec { + zasynclock!(self.state.multicast.transports) + .values() + .map(|t| t.locator.clone().into()) + .collect() + } + + pub async fn get_locators_multicast(&self) -> Vec { + zasynclock!(self.state.multicast.transports) + .values() + .map(|t| t.locator.clone()) + .collect() + } } diff --git a/io/zenoh-transport/src/multicast/mod.rs b/io/zenoh-transport/src/multicast/mod.rs index 4fb1b0a592..9c1d8646f3 100644 --- a/io/zenoh-transport/src/multicast/mod.rs +++ b/io/zenoh-transport/src/multicast/mod.rs @@ -10,73 +10,47 @@ // // Contributors: // ZettaScale Zenoh Team, -// pub(crate) mod authenticator; pub(crate) mod establishment; pub(crate) mod link; pub(crate) mod manager; pub(crate) mod rx; +#[cfg(feature = "shared-memory")] +pub(crate) mod shm; pub(crate) mod transport; pub(crate) mod tx; use super::common; -#[cfg(feature = "stats")] -use super::common::stats::stats_struct; use crate::{TransportMulticastEventHandler, TransportPeer}; -pub use manager::*; +pub use manager::{ + TransportManagerBuilderMulticast, TransportManagerConfigMulticast, + TransportManagerParamsMulticast, +}; use std::{ fmt, sync::{Arc, Weak}, }; -use transport::{TransportMulticastConfig, TransportMulticastInner}; -use zenoh_core::zread; -use zenoh_link::Link; -use zenoh_protocol::{core::ZInt, transport::tmsg, zenoh::ZenohMessage}; +use transport::TransportMulticastInner; +use zenoh_core::{zcondfeat, zread}; +use zenoh_link::{Link, LinkMulticast}; +use zenoh_protocol::{ + core::Bits, + network::NetworkMessage, + transport::{close, PrioritySn}, +}; use zenoh_result::{zerror, ZResult}; /*************************************/ -/* STATS */ +/* TRANSPORT MULTICAST */ /*************************************/ -#[cfg(feature = "stats")] -use serde::{Deserialize, Serialize}; -#[cfg(feature = "stats")] -use std::sync::atomic::{AtomicUsize, Ordering}; -#[cfg(feature = "stats")] -stats_struct! { - #[derive(Clone, Debug, Deserialize, Serialize)] - pub struct TransportMulticastStats { - pub tx_t_msgs, - pub tx_z_msgs, - pub tx_z_dropped, - pub tx_z_data_msgs, - pub tx_z_data_payload_bytes, - pub tx_z_data_reply_msgs, - pub tx_z_data_reply_payload_bytes, - pub tx_z_pull_msgs, - pub tx_z_query_msgs, - pub tx_z_declare_msgs, - pub tx_z_linkstate_msgs, - pub tx_z_unit_msgs, - pub tx_z_unit_reply_msgs, - pub tx_bytes, - pub rx_t_msgs, - pub rx_z_msgs, - pub rx_z_data_msgs, - pub rx_z_data_payload_bytes, - pub rx_z_data_reply_msgs, - pub rx_z_data_reply_payload_bytes, - pub rx_z_pull_msgs, - pub rx_z_query_msgs, - pub rx_z_declare_msgs, - pub rx_z_linkstate_msgs, - pub rx_z_unit_msgs, - pub rx_z_unit_reply_msgs, - pub rx_bytes, - } +#[derive(Clone, Debug, PartialEq, Eq)] +pub(crate) struct TransportConfigMulticast { + pub(crate) sn_resolution: Bits, + pub(crate) initial_sns: Box<[PrioritySn]>, + pub(crate) link: LinkMulticast, + #[cfg(feature = "shared-memory")] + pub(crate) is_shm: bool, } -/*************************************/ -/* TRANSPORT MULTICAST */ -/*************************************/ #[derive(Clone)] pub struct TransportMulticast(Weak); @@ -89,11 +63,12 @@ impl TransportMulticast { } #[inline(always)] - pub fn get_sn_resolution(&self) -> ZResult { + pub fn get_sn_resolution(&self) -> ZResult { let transport = self.get_transport()?; Ok(transport.get_sn_resolution()) } + #[cfg(feature = "shared-memory")] #[inline(always)] pub fn is_shm(&self) -> ZResult { let transport = self.get_transport()?; @@ -128,26 +103,26 @@ impl TransportMulticast { pub async fn close(&self) -> ZResult<()> { // Return Ok if the transport has already been closed match self.get_transport() { - Ok(transport) => transport.close(tmsg::close_reason::GENERIC).await, + Ok(transport) => transport.close(close::reason::GENERIC).await, Err(_) => Ok(()), } } #[inline(always)] - pub fn schedule(&self, message: ZenohMessage) -> ZResult<()> { + pub fn schedule(&self, message: NetworkMessage) -> ZResult<()> { let transport = self.get_transport()?; transport.schedule(message); Ok(()) } #[inline(always)] - pub fn handle_message(&self, message: ZenohMessage) -> ZResult<()> { + pub fn handle_message(&self, message: NetworkMessage) -> ZResult<()> { self.schedule(message) } #[cfg(feature = "stats")] - pub fn get_stats(&self) -> ZResult { - Ok(self.get_transport()?.stats.snapshot()) + pub fn get_stats(&self) -> ZResult> { + Ok(self.get_transport()?.stats.clone()) } } @@ -169,6 +144,7 @@ impl fmt::Debug for TransportMulticast { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.get_transport() { Ok(transport) => { + let is_shm = zcondfeat!("shared-memory", transport.is_shm(), false); let peers: String = zread!(transport.peers) .iter() .map(|(l, p)| { @@ -179,7 +155,7 @@ impl fmt::Debug for TransportMulticast { f.debug_struct("Transport Multicast") .field("sn_resolution", &transport.get_sn_resolution()) .field("is_qos", &transport.is_qos()) - .field("is_shm", &transport.is_shm()) + .field("is_shm", &is_shm) .field("peers", &peers) .finish() } diff --git a/io/zenoh-transport/src/multicast/rx.rs b/io/zenoh-transport/src/multicast/rx.rs index 89bc7ba329..8dd4882ded 100644 --- a/io/zenoh-transport/src/multicast/rx.rs +++ b/io/zenoh-transport/src/multicast/rx.rs @@ -11,17 +11,19 @@ // Contributors: // ZettaScale Zenoh Team, // -use super::common::conduit::TransportChannelRx; use super::transport::{TransportMulticastInner, TransportMulticastPeer}; +use crate::common::priority::TransportChannelRx; use std::sync::MutexGuard; +use zenoh_buffers::reader::{HasReader, Reader}; +use zenoh_buffers::ZSlice; +use zenoh_codec::{RCodec, Zenoh080}; use zenoh_core::{zlock, zread}; -#[cfg(feature = "stats")] -use zenoh_protocol::zenoh::ZenohBody; -use zenoh_protocol::{ - core::{Locator, Priority, Reliability, ZInt}, - transport::{Frame, FramePayload, Join, TransportBody, TransportMessage}, - zenoh::ZenohMessage, +use zenoh_link::LinkMulticast; +use zenoh_protocol::core::{Priority, Reliability}; +use zenoh_protocol::transport::{ + BatchSize, Close, Fragment, Frame, Join, KeepAlive, TransportBody, TransportSn, }; +use zenoh_protocol::{core::Locator, network::NetworkMessage, transport::TransportMessage}; use zenoh_result::{bail, zerror, ZResult}; /*************************************/ @@ -32,97 +34,17 @@ impl TransportMulticastInner { fn trigger_callback( &self, #[allow(unused_mut)] // shared-memory feature requires mut - mut msg: ZenohMessage, + mut msg: NetworkMessage, peer: &TransportMulticastPeer, ) -> ZResult<()> { - #[cfg(feature = "stats")] - { - use zenoh_buffers::SplitBuffer; - self.stats.inc_rx_z_msgs(1); - match &msg.body { - ZenohBody::Data(data) => match data.reply_context { - Some(_) => { - self.stats.inc_rx_z_data_reply_msgs(1); - self.stats - .inc_rx_z_data_reply_payload_bytes(data.payload.len()); - } - None => { - self.stats.inc_rx_z_data_msgs(1); - self.stats.inc_rx_z_data_payload_bytes(data.payload.len()); - } - }, - ZenohBody::Unit(unit) => match unit.reply_context { - Some(_) => self.stats.inc_rx_z_unit_reply_msgs(1), - None => self.stats.inc_rx_z_unit_msgs(1), - }, - ZenohBody::Pull(_) => self.stats.inc_rx_z_pull_msgs(1), - ZenohBody::Query(_) => self.stats.inc_rx_z_query_msgs(1), - ZenohBody::Declare(_) => self.stats.inc_rx_z_declare_msgs(1), - ZenohBody::LinkStateList(_) => self.stats.inc_rx_z_linkstate_msgs(1), - } - } - #[cfg(feature = "shared-memory")] { - let _ = crate::shm::map_zmsg_to_shmbuf(&mut msg, &self.manager.shmr)?; - } - - peer.handler.handle_message(msg) - } - - fn handle_frame( - &self, - sn: ZInt, - payload: FramePayload, - mut guard: MutexGuard<'_, TransportChannelRx>, - peer: &TransportMulticastPeer, - ) -> ZResult<()> { - let precedes = guard.sn.precedes(sn)?; - if !precedes { - log::debug!( - "Transport: {}. Frame with invalid SN dropped: {}. Expected: {}.", - self.manager.config.zid, - sn, - guard.sn.next(), - ); - // Drop the fragments if needed - if !guard.defrag.is_empty() { - guard.defrag.clear(); + if self.manager.config.multicast.is_shm { + crate::shm::map_zmsg_to_shmbuf(&mut msg, &self.manager.state.multicast.shm.reader)?; } - // Keep reading - return Ok(()); } - // Set will always return OK because we have already checked - // with precedes() that the sn has the right resolution - let _ = guard.sn.set(sn); - match payload { - FramePayload::Fragment { buffer, is_final } => { - if guard.defrag.is_empty() { - let _ = guard.defrag.sync(sn); - } - guard.defrag.push(sn, buffer)?; - if is_final { - // When shared-memory feature is disabled, msg does not need to be mutable - let msg = guard.defrag.defragment().ok_or_else(|| { - zerror!( - "Transport {}: {}. Defragmentation error.", - self.manager.config.zid, - self.locator - ) - })?; - self.trigger_callback(msg, peer) - } else { - Ok(()) - } - } - FramePayload::Messages { mut messages } => { - for msg in messages.drain(..) { - self.trigger_callback(msg, peer)?; - } - Ok(()) - } - } + peer.handler.handle_message(msg) } pub(super) fn handle_join_from_peer( @@ -134,12 +56,12 @@ impl TransportMulticastInner { if join.version != peer.version || join.zid != peer.zid || join.whatami != peer.whatami - || join.sn_resolution != peer.sn_resolution + || join.resolution != peer.resolution || join.lease != peer.lease - || join.is_qos() != peer.is_qos() + || join.ext_qos.is_some() != peer.is_qos() { let e = format!( - "Ingoring Join on {} of peer: {}. Inconsistent parameters. Version", + "Ingoring Join on {} of peer: {}. Inconsistent parameters.", peer.locator, peer.zid, ); log::debug!("{}", e); @@ -149,7 +71,12 @@ impl TransportMulticastInner { Ok(()) } - pub(super) fn handle_join_from_unknown(&self, join: Join, locator: &Locator) -> ZResult<()> { + pub(super) fn handle_join_from_unknown( + &self, + join: Join, + locator: &Locator, + batch_size: BatchSize, + ) -> ZResult<()> { if zread!(self.peers).len() >= self.manager.config.multicast.max_sessions { log::debug!( "Ingoring Join on {} from peer: {}. Max sessions reached: {}.", @@ -171,18 +98,29 @@ impl TransportMulticastInner { return Ok(()); } - if join.sn_resolution > self.manager.config.sn_resolution { + if join.resolution != self.manager.config.resolution { log::debug!( - "Ingoring Join on {} from peer: {}. Unsupported SN resolution: {}. Expected: <= {}.", + "Ingoring Join on {} from peer: {}. Unsupported SN resolution: {:?}. Expected: {:?}.", locator, join.zid, - join.sn_resolution, - self.manager.config.sn_resolution, + join.resolution, + self.manager.config.resolution, ); return Ok(()); } - if !self.manager.config.multicast.is_qos && join.is_qos() { + if join.batch_size != batch_size { + log::debug!( + "Ingoring Join on {} from peer: {}. Unsupported Batch Size: {:?}. Expected: {:?}.", + locator, + join.zid, + join.batch_size, + batch_size, + ); + return Ok(()); + } + + if !self.manager.config.multicast.is_qos && join.ext_qos.is_some() { log::debug!( "Ingoring Join on {} from peer: {}. QoS is not supported.", locator, @@ -191,59 +129,177 @@ impl TransportMulticastInner { return Ok(()); } - let _ = self.new_peer(locator, join); + self.new_peer(locator, join) + } + + fn handle_frame(&self, frame: Frame, peer: &TransportMulticastPeer) -> ZResult<()> { + let Frame { + reliability, + sn, + ext_qos, + mut payload, + } = frame; + + let priority = ext_qos.priority(); + let c = if self.is_qos() { + &peer.priority_rx[priority as usize] + } else if priority == Priority::default() { + &peer.priority_rx[0] + } else { + bail!( + "Transport: {}. Peer: {}. Unknown priority: {:?}.", + self.manager.config.zid, + peer.zid, + priority + ); + }; + + let mut guard = match reliability { + Reliability::Reliable => zlock!(c.reliable), + Reliability::BestEffort => zlock!(c.best_effort), + }; + + self.verify_sn(sn, &mut guard)?; + + for msg in payload.drain(..) { + self.trigger_callback(msg, peer)?; + } + Ok(()) + } + + fn handle_fragment(&self, fragment: Fragment, peer: &TransportMulticastPeer) -> ZResult<()> { + let Fragment { + reliability, + more, + sn, + ext_qos, + payload, + } = fragment; + + let priority = ext_qos.priority(); + let c = if self.is_qos() { + &peer.priority_rx[priority as usize] + } else if priority == Priority::default() { + &peer.priority_rx[0] + } else { + bail!( + "Transport: {}. Peer: {}. Unknown priority: {:?}.", + self.manager.config.zid, + peer.zid, + priority + ); + }; + + let mut guard = match reliability { + Reliability::Reliable => zlock!(c.reliable), + Reliability::BestEffort => zlock!(c.best_effort), + }; + + self.verify_sn(sn, &mut guard)?; + + if guard.defrag.is_empty() { + let _ = guard.defrag.sync(sn); + } + guard.defrag.push(sn, payload)?; + if !more { + // When shared-memory feature is disabled, msg does not need to be mutable + let msg = guard.defrag.defragment().ok_or_else(|| { + zerror!( + "Transport: {}. Peer: {}. Priority: {:?}. Defragmentation error.", + self.manager.config.zid, + peer.zid, + priority + ) + })?; + return self.trigger_callback(msg, peer); + } + + Ok(()) + } + + fn verify_sn( + &self, + sn: TransportSn, + guard: &mut MutexGuard<'_, TransportChannelRx>, + ) -> ZResult<()> { + let precedes = guard.sn.precedes(sn)?; + if !precedes { + log::debug!( + "Transport: {}. Frame with invalid SN dropped: {}. Expected: {}.", + self.manager.config.zid, + sn, + guard.sn.next() + ); + // Drop the fragments if needed + if !guard.defrag.is_empty() { + guard.defrag.clear(); + } + // Keep reading + return Ok(()); + } + + // Set will always return OK because we have already checked + // with precedes() that the sn has the right resolution + let _ = guard.sn.set(sn); Ok(()) } - pub(super) fn receive_message(&self, msg: TransportMessage, locator: &Locator) -> ZResult<()> { - // Process the received message - let r_guard = zread!(self.peers); - match r_guard.get(locator) { - Some(peer) => { - peer.active(); - - match msg.body { - TransportBody::Frame(Frame { - channel, - sn, - payload, - }) => { - let c = if self.is_qos() { - &peer.conduit_rx[channel.priority as usize] - } else if channel.priority == Priority::default() { - &peer.conduit_rx[0] - } else { - bail!( - "Transport {}: {}. Unknown conduit {:?} from {}.", + pub(super) fn read_messages( + &self, + mut zslice: ZSlice, + link: &LinkMulticast, + batch_size: BatchSize, + locator: &Locator, + #[cfg(feature = "stats")] transport: &TransportMulticastInner, + ) -> ZResult<()> { + let codec = Zenoh080::new(); + let mut reader = zslice.reader(); + while reader.can_read() { + let msg: TransportMessage = codec + .read(&mut reader) + .map_err(|_| zerror!("{}: decoding error", link))?; + + log::trace!("Received: {:?}", msg); + + #[cfg(feature = "stats")] + { + transport.stats.inc_rx_t_msgs(1); + } + + let r_guard = zread!(self.peers); + match r_guard.get(locator) { + Some(peer) => { + peer.active(); + match msg.body { + TransportBody::Frame(msg) => self.handle_frame(msg, peer)?, + TransportBody::Fragment(fragment) => { + self.handle_fragment(fragment, peer)? + } + TransportBody::Join(join) => self.handle_join_from_peer(join, peer)?, + TransportBody::KeepAlive(KeepAlive { .. }) => {} + TransportBody::Close(Close { reason, .. }) => { + drop(r_guard); + self.del_peer(locator, reason)?; + } + _ => { + log::debug!( + "Transport: {}. Message handling not implemented: {:?}", self.manager.config.zid, - self.locator, - channel.priority, - peer.locator + msg ); - }; - - let guard = match channel.reliability { - Reliability::Reliable => zlock!(c.reliable), - Reliability::BestEffort => zlock!(c.best_effort), - }; - self.handle_frame(sn, payload, guard, peer) - } - TransportBody::Join(join) => self.handle_join_from_peer(join, peer), - TransportBody::Close(close) => { - drop(r_guard); - self.del_peer(locator, close.reason) + } } - _ => Ok(()), } - } - None => { - drop(r_guard); - match msg.body { - TransportBody::Join(join) => self.handle_join_from_unknown(join, locator), - _ => Ok(()), + None => { + drop(r_guard); + if let TransportBody::Join(join) = msg.body { + self.handle_join_from_unknown(join, locator, batch_size)?; + } } } } + + Ok(()) } } diff --git a/io/zenoh-transport/src/multicast/shm.rs b/io/zenoh-transport/src/multicast/shm.rs new file mode 100644 index 0000000000..4e4c84d2a7 --- /dev/null +++ b/io/zenoh-transport/src/multicast/shm.rs @@ -0,0 +1,44 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use async_std::sync::RwLock; +use rand::{Rng, SeedableRng}; +use zenoh_crypto::PseudoRng; +use zenoh_result::ZResult; +use zenoh_shm::{SharedMemoryManager, SharedMemoryReader}; + +pub(crate) type Challenge = u64; +const NAME: &str = "zshm_mcast"; + +pub(crate) struct SharedMemoryMulticast { + pub(crate) _manager: SharedMemoryManager, + pub(crate) reader: RwLock, +} + +unsafe impl Sync for SharedMemoryMulticast {} + +impl SharedMemoryMulticast { + pub fn make() -> ZResult { + let mut prng = PseudoRng::from_entropy(); + let nonce = prng.gen::(); + let size = std::mem::size_of::(); + + let mut _manager = SharedMemoryManager::make(format!("{NAME}.{nonce}"), size)?; + + let shmauth = SharedMemoryMulticast { + _manager, + reader: RwLock::new(SharedMemoryReader::new()), + }; + Ok(shmauth) + } +} diff --git a/io/zenoh-transport/src/multicast/transport.rs b/io/zenoh-transport/src/multicast/transport.rs index b16b19e36c..67c3ac268d 100644 --- a/io/zenoh-transport/src/multicast/transport.rs +++ b/io/zenoh-transport/src/multicast/transport.rs @@ -11,12 +11,13 @@ // Contributors: // ZettaScale Zenoh Team, // -use super::common::conduit::{TransportConduitRx, TransportConduitTx}; +use super::common::priority::{TransportPriorityRx, TransportPriorityTx}; use super::link::{TransportLinkMulticast, TransportLinkMulticastConfig}; #[cfg(feature = "stats")] -use super::TransportMulticastStatsAtomic; +use crate::stats::TransportStats; use crate::{ - TransportManager, TransportMulticastEventHandler, TransportPeer, TransportPeerEventHandler, + TransportConfigMulticast, TransportManager, TransportMulticastEventHandler, TransportPeer, + TransportPeerEventHandler, }; use async_trait::async_trait; use std::{ @@ -27,12 +28,13 @@ use std::{ }, time::Duration, }; -use zenoh_core::{zread, zwrite}; +use zenoh_core::{zcondfeat, zread, zwrite}; use zenoh_link::{Link, LinkMulticast, Locator}; +use zenoh_protocol::core::Resolution; +use zenoh_protocol::transport::{batch_size, Close, TransportMessage}; use zenoh_protocol::{ - core::{ConduitSnList, Priority, WhatAmI, ZInt, ZenohId}, - transport::{tmsg, Join, TransportMessage}, - zenoh::ZenohMessage, + core::{Bits, Field, Priority, WhatAmI, ZenohId}, + transport::{close, Join}, }; use zenoh_result::{bail, ZResult}; use zenoh_util::{Timed, TimedEvent, TimedHandle, Timer}; @@ -46,11 +48,11 @@ pub(super) struct TransportMulticastPeer { pub(super) locator: Locator, pub(super) zid: ZenohId, pub(super) whatami: WhatAmI, - pub(super) sn_resolution: ZInt, + pub(super) resolution: Resolution, pub(super) lease: Duration, pub(super) whatchdog: Arc, pub(super) handle: TimedHandle, - pub(super) conduit_rx: Box<[TransportConduitRx]>, + pub(super) priority_rx: Box<[TransportPriorityRx]>, pub(super) handler: Arc, } @@ -60,7 +62,7 @@ impl TransportMulticastPeer { } pub(super) fn is_qos(&self) -> bool { - self.conduit_rx.len() == Priority::NUM + self.priority_rx.len() == Priority::NUM } } @@ -78,7 +80,7 @@ impl Timed for TransportMulticastPeerLeaseTimer { if !is_active { let _ = self .transport - .del_peer(&self.locator, tmsg::close_reason::EXPIRED); + .del_peer(&self.locator, close::reason::EXPIRED); } } } @@ -87,12 +89,12 @@ impl Timed for TransportMulticastPeerLeaseTimer { pub(crate) struct TransportMulticastInner { // The manager this channel is associated to pub(super) manager: TransportManager, - // The multicast locator - pub(super) locator: Locator, - // Tx conduits - pub(super) conduit_tx: Arc<[TransportConduitTx]>, + // Tx priorities + pub(super) priority_tx: Arc<[TransportPriorityTx]>, // Remote peers pub(super) peers: Arc>>, + // The multicast locator - Convenience for logging + pub(super) locator: Locator, // The multicast link pub(super) link: Arc>>, // The callback @@ -101,49 +103,41 @@ pub(crate) struct TransportMulticastInner { pub(super) timer: Arc, // Transport statistics #[cfg(feature = "stats")] - pub(super) stats: Arc, -} - -pub(crate) struct TransportMulticastConfig { - pub(crate) manager: TransportManager, - pub(crate) initial_sns: ConduitSnList, - pub(crate) link: LinkMulticast, + pub(super) stats: Arc, } impl TransportMulticastInner { - pub(super) fn make(config: TransportMulticastConfig) -> ZResult { - let mut conduit_tx = vec![]; - - match config.initial_sns { - ConduitSnList::Plain(sn) => { - let tct = TransportConduitTx::make(config.manager.config.sn_resolution)?; - tct.sync(sn)?; - conduit_tx.push(tct); - } - ConduitSnList::QoS(sns) => { - for (_, sn) in sns.iter().enumerate() { - let tct = TransportConduitTx::make(config.manager.config.sn_resolution)?; - tct.sync(*sn)?; - conduit_tx.push(tct); - } + pub(super) fn make( + manager: TransportManager, + config: TransportConfigMulticast, + ) -> ZResult { + let mut priority_tx = vec![]; + if (config.initial_sns.len() != 1) != (config.initial_sns.len() != Priority::NUM) { + for (_, sn) in config.initial_sns.iter().enumerate() { + let tct = TransportPriorityTx::make(config.sn_resolution)?; + tct.sync(*sn)?; + priority_tx.push(tct); } + } else { + bail!("Invalid QoS configuration"); } let ti = TransportMulticastInner { - manager: config.manager, - locator: config.link.get_dst().to_owned(), - conduit_tx: conduit_tx.into_boxed_slice().into(), + manager, + priority_tx: priority_tx.into_boxed_slice().into(), peers: Arc::new(RwLock::new(HashMap::new())), + locator: config.link.get_dst().to_owned(), link: Arc::new(RwLock::new(None)), callback: Arc::new(RwLock::new(None)), timer: Arc::new(Timer::new(false)), #[cfg(feature = "stats")] - stats: Arc::new(TransportMulticastStatsAtomic::default()), + stats: Arc::new(TransportStats::default()), }; - let mut w_guard = zwrite!(ti.link); - *w_guard = Some(TransportLinkMulticast::new(ti.clone(), config.link)); - drop(w_guard); + let link = TransportLinkMulticast::new(ti.clone(), config.link); + let mut guard = zwrite!(ti.link); + *guard = Some(link); + drop(guard); Ok(ti) } @@ -156,16 +150,17 @@ impl TransportMulticastInner { /*************************************/ /* ACCESSORS */ /*************************************/ - pub(crate) fn get_sn_resolution(&self) -> ZInt { - self.manager.config.sn_resolution + pub(crate) fn get_sn_resolution(&self) -> Bits { + self.manager.config.resolution.get(Field::FrameSN) } pub(crate) fn is_qos(&self) -> bool { - self.conduit_tx.len() > 1 + self.priority_tx.len() == Priority::NUM } + #[cfg(feature = "shared-memory")] pub(crate) fn is_shm(&self) -> bool { - false + self.manager.config.multicast.is_shm } pub(crate) fn get_callback(&self) -> Option> { @@ -180,7 +175,7 @@ impl TransportMulticastInner { /* TERMINATION */ /*************************************/ pub(super) async fn delete(&self) -> ZResult<()> { - log::debug!("Closing multicast transport on {}", self.locator); + log::debug!("Closing multicast transport on {:?}", self.locator); // Notify the callback that we are going to close the transport let callback = zwrite!(self.callback).take(); @@ -189,7 +184,7 @@ impl TransportMulticastInner { } // Delete the transport on the manager - let _ = self.manager.del_transport_multicast(&self.locator); + let _ = self.manager.del_transport_multicast(&self.locator).await; // Close all the links let mut link = zwrite!(self.link).take(); @@ -212,56 +207,42 @@ impl TransportMulticastInner { self.locator ); - let pipeline = zread!(self.link) - .as_ref() - .unwrap() - .pipeline - .as_ref() - .unwrap() - .clone(); - - // Close message to be sent on all the links - let peer_id = Some(self.manager.zid()); - let reason_id = reason; - // link_only should always be false for user-triggered close. However, in case of - // multiple links, it is safer to close all the links first. When no links are left, - // the transport is then considered closed. - let link_only = true; - let attachment = None; // No attachment here - let msg = TransportMessage::make_close(peer_id, reason_id, link_only, attachment); - - pipeline.push_transport_message(msg, Priority::Background); - - // Terminate and clean up the transport - self.delete().await - } - - /*************************************/ - /* SCHEDULE AND SEND TX */ - /*************************************/ - /// Schedule a Zenoh message on the transmission queue - #[allow(unused_mut)] // Required with "shared-memory" feature - pub(crate) fn schedule(&self, mut msg: ZenohMessage) { - // Multicast transports do not support SHM for the time being - #[cfg(feature = "shared-memory")] { - let res = crate::shm::map_zmsg_to_shmbuf(&mut msg, &self.manager.shmr); - if let Err(e) = res { - log::trace!("Failed SHM conversion: {}", e); - return; + let r_guard = zread!(self.link); + if let Some(link) = r_guard.as_ref() { + if let Some(pipeline) = link.pipeline.as_ref() { + let pipeline = pipeline.clone(); + drop(r_guard); + // Close message to be sent on all the links + let msg: TransportMessage = Close { + reason, + session: false, + } + .into(); + pipeline.push_transport_message(msg, Priority::Background); + } } } - self.schedule_first_fit(msg); + + // Terminate and clean up the transport + self.delete().await } /*************************************/ /* LINK */ /*************************************/ - pub(super) fn start_tx(&self, batch_size: u16) -> ZResult<()> { + pub(super) fn start_tx(&self) -> ZResult<()> { let mut guard = zwrite!(self.link); match guard.as_mut() { Some(l) => { - assert!(!self.conduit_tx.is_empty()); + // For cross-system compatibility reasons we set the default minimal + // batch size to 8192 bytes unless explicitly configured smaller. + let batch_size = self + .manager + .config + .batch_size + .min(l.link.get_mtu()) + .min(batch_size::MULTICAST); let config = TransportLinkMulticastConfig { version: self.manager.config.version, zid: self.manager.config.zid, @@ -269,10 +250,10 @@ impl TransportMulticastInner { lease: self.manager.config.multicast.lease, keep_alive: self.manager.config.multicast.keep_alive, join_interval: self.manager.config.multicast.join_interval, - sn_resolution: self.manager.config.sn_resolution, + sn_resolution: self.manager.config.resolution.get(Field::FrameSN), batch_size, }; - l.start_tx(config, self.conduit_tx.clone()); + l.start_tx(config, self.priority_tx.clone()); Ok(()) } None => { @@ -306,7 +287,15 @@ impl TransportMulticastInner { let mut guard = zwrite!(self.link); match guard.as_mut() { Some(l) => { - l.start_rx(); + // For cross-system compatibility reasons we set the default minimal + // batch size to 8192 bytes unless explicitly configured smaller. + let batch_size = self + .manager + .config + .batch_size + .min(l.link.get_mtu()) + .min(batch_size::MULTICAST); + l.start_rx(batch_size); Ok(()) } None => { @@ -343,11 +332,13 @@ impl TransportMulticastInner { let mut link = Link::from(self.get_link()); link.dst = locator.clone(); + let is_shm = zcondfeat!("shared-memory", join.ext_shm.is_some(), false); let peer = TransportPeer { zid: join.zid, whatami: join.whatami, - is_qos: join.is_qos(), - is_shm: self.is_shm(), + is_qos: join.ext_qos.is_some(), + #[cfg(feature = "shared-memory")] + is_shm, links: vec![link], }; @@ -356,30 +347,36 @@ impl TransportMulticastInner { None => return Ok(()), }; - let conduit_rx = match join.next_sns { - ConduitSnList::Plain(sn) => { - let tcr = TransportConduitRx::make( - join.sn_resolution, - self.manager.config.defrag_buff_size, - )?; - tcr.sync(sn)?; - vec![tcr] - } - ConduitSnList::QoS(ref sns) => { - let mut tcrs = Vec::with_capacity(sns.len()); - for (_, sn) in sns.iter().enumerate() { - let tcr = TransportConduitRx::make( - join.sn_resolution, - self.manager.config.defrag_buff_size, - )?; - tcr.sync(*sn)?; - tcrs.push(tcr); - } - tcrs - } + // Build next SNs + let next_sns = match join.ext_qos.as_ref() { + Some(sns) => sns.to_vec(), + None => vec![join.next_sn], } .into_boxed_slice(); + let mut priority_rx = Vec::with_capacity(next_sns.len()); + for (_, sn) in next_sns.iter().enumerate() { + let tprx = TransportPriorityRx::make( + join.resolution.get(Field::FrameSN), + self.manager.config.defrag_buff_size, + )?; + tprx.sync(*sn)?; + priority_rx.push(tprx); + } + let priority_rx = priority_rx.into_boxed_slice(); + + log::debug!( + "New transport joined on {}: zid {}, whatami {}, resolution {:?}, locator {}, is_qos {}, is_shm {}, initial sn: {:?}", + self.locator, + peer.zid, + peer.whatami, + join.resolution, + locator, + peer.is_qos, + is_shm, + next_sns, + ); + // Create lease event let whatchdog = Arc::new(AtomicBool::new(false)); let event = TransportMulticastPeerLeaseTimer { @@ -396,31 +393,18 @@ impl TransportMulticastInner { locator: locator.clone(), zid: peer.zid, whatami: peer.whatami, - sn_resolution: join.sn_resolution, + resolution: join.resolution, lease: join.lease, whatchdog, handle, - conduit_rx, + priority_rx, handler, }; - { - zwrite!(self.peers).insert(locator.clone(), peer); - } + zwrite!(self.peers).insert(locator.clone(), peer); // Add the event to the timer self.timer.add(event); - log::debug!( - "New transport joined on {}: zid {}, whatami {}, sn resolution {}, locator {}, qos {}, initial sn: {}", - self.locator, - join.zid, - join.whatami, - join.sn_resolution, - locator, - join.is_qos(), - join.next_sns, - ); - Ok(()) } @@ -455,6 +439,7 @@ impl TransportMulticastInner { zid: p.zid, whatami: p.whatami, is_qos: p.is_qos(), + #[cfg(feature = "shared-memory")] is_shm: self.is_shm(), links: vec![link], } diff --git a/io/zenoh-transport/src/multicast/tx.rs b/io/zenoh-transport/src/multicast/tx.rs index baa266201a..74238c7e71 100644 --- a/io/zenoh-transport/src/multicast/tx.rs +++ b/io/zenoh-transport/src/multicast/tx.rs @@ -13,13 +13,11 @@ // use super::transport::TransportMulticastInner; use zenoh_core::zread; -#[cfg(feature = "stats")] -use zenoh_protocol::zenoh::ZenohBody; -use zenoh_protocol::zenoh::ZenohMessage; +use zenoh_protocol::network::NetworkMessage; //noinspection ALL impl TransportMulticastInner { - fn schedule_on_link(&self, msg: ZenohMessage) -> bool { + fn schedule_on_link(&self, msg: NetworkMessage) -> bool { macro_rules! zpush { ($guard:expr, $pipeline:expr, $msg:expr) => { // Drop the guard before the push_zenoh_message since @@ -27,7 +25,7 @@ impl TransportMulticastInner { // block for fairly long time let pl = $pipeline.clone(); drop($guard); - return pl.push_zenoh_message($msg); + return pl.push_network_message($msg); }; } @@ -49,41 +47,30 @@ impl TransportMulticastInner { false } + #[allow(unused_mut)] // When feature "shared-memory" is not enabled #[allow(clippy::let_and_return)] // When feature "stats" is not enabled #[inline(always)] - pub(super) fn schedule_first_fit(&self, msg: ZenohMessage) -> bool { - #[cfg(feature = "stats")] - use zenoh_buffers::SplitBuffer; - #[cfg(feature = "stats")] - match &msg.body { - ZenohBody::Data(data) => match data.reply_context { - Some(_) => { - self.stats.inc_tx_z_data_reply_msgs(1); - self.stats - .inc_tx_z_data_reply_payload_bytes(data.payload.len()); - } - None => { - self.stats.inc_tx_z_data_msgs(1); - self.stats.inc_tx_z_data_payload_bytes(data.payload.len()); - } - }, - ZenohBody::Unit(unit) => match unit.reply_context { - Some(_) => self.stats.inc_tx_z_unit_reply_msgs(1), - None => self.stats.inc_tx_z_unit_msgs(1), - }, - ZenohBody::Pull(_) => self.stats.inc_tx_z_pull_msgs(1), - ZenohBody::Query(_) => self.stats.inc_tx_z_query_msgs(1), - ZenohBody::Declare(_) => self.stats.inc_tx_z_declare_msgs(1), - ZenohBody::LinkStateList(_) => self.stats.inc_tx_z_linkstate_msgs(1), + pub(super) fn schedule(&self, mut msg: NetworkMessage) -> bool { + #[cfg(feature = "shared-memory")] + { + let res = if self.manager.config.multicast.is_shm { + crate::shm::map_zmsg_to_shminfo(&mut msg) + } else { + crate::shm::map_zmsg_to_shmbuf(&mut msg, &self.manager.state.multicast.shm.reader) + }; + if let Err(e) = res { + log::trace!("Failed SHM conversion: {}", e); + return false; + } } let res = self.schedule_on_link(msg); #[cfg(feature = "stats")] if res { - self.stats.inc_tx_z_msgs(1); + self.stats.inc_tx_n_msgs(1); } else { - self.stats.inc_tx_z_dropped(1); + self.stats.inc_tx_n_dropped(1); } res diff --git a/io/zenoh-transport/src/primitives/demux.rs b/io/zenoh-transport/src/primitives/demux.rs index 942b8c13d8..260fffa11d 100644 --- a/io/zenoh-transport/src/primitives/demux.rs +++ b/io/zenoh-transport/src/primitives/demux.rs @@ -15,10 +15,8 @@ use super::Primitives; use crate::TransportPeerEventHandler; use std::any::Any; use zenoh_link::Link; -use zenoh_protocol::zenoh::{ - Data, Declaration, Declare, LinkStateList, Pull, Query, Unit, ZenohBody, ZenohMessage, -}; -use zenoh_result::{bail, ZResult}; +use zenoh_protocol::network::{NetworkBody, NetworkMessage}; +use zenoh_result::ZResult; pub struct DeMux { primitives: P, @@ -31,111 +29,14 @@ impl DeMux

{ } impl TransportPeerEventHandler for DeMux

{ - fn handle_message(&self, msg: ZenohMessage) -> ZResult<()> { + fn handle_message(&self, msg: NetworkMessage) -> ZResult<()> { match msg.body { - ZenohBody::Declare(Declare { declarations, .. }) => { - for declaration in declarations { - match declaration { - Declaration::Resource(r) => { - self.primitives.decl_resource(r.expr_id, &r.key); - } - Declaration::Publisher(p) => { - self.primitives.decl_publisher(&p.key, msg.routing_context); - } - Declaration::Subscriber(s) => { - self.primitives - .decl_subscriber(&s.key, &s.info, msg.routing_context); - } - Declaration::Queryable(q) => { - self.primitives - .decl_queryable(&q.key, &q.info, msg.routing_context); - } - Declaration::ForgetResource(fr) => { - self.primitives.forget_resource(fr.expr_id); - } - Declaration::ForgetPublisher(fp) => { - self.primitives - .forget_publisher(&fp.key, msg.routing_context); - } - Declaration::ForgetSubscriber(fs) => { - self.primitives - .forget_subscriber(&fs.key, msg.routing_context); - } - Declaration::ForgetQueryable(q) => { - self.primitives - .forget_queryable(&q.key, msg.routing_context); - } - } - } - } - - ZenohBody::Data(Data { - key, - data_info, - payload, - congestion_control, - reply_context, - }) => match reply_context { - None => { - self.primitives.send_data( - &key, - payload, - msg.channel, - congestion_control, - data_info, - msg.routing_context, - ); - } - Some(rep) => match rep.replier { - Some(replier) => { - self.primitives - .send_reply_data(rep.qid, replier.id, key, data_info, payload); - } - None => { - bail!("ReplyData with no replier_id") - } - }, - }, - - ZenohBody::Unit(Unit { reply_context, .. }) => { - if let Some(rep) = reply_context { - if rep.is_final() { - self.primitives.send_reply_final(rep.qid); - } - } - } - - ZenohBody::Query(Query { - key, - parameters, - qid, - target, - consolidation, - body, - .. - }) => { - self.primitives.send_query( - &key, - ¶meters, - qid, - target.unwrap_or_default(), - consolidation, - body, - msg.routing_context, - ); - } - - ZenohBody::Pull(Pull { - key, - pull_id, - max_samples, - is_final, - }) => { - self.primitives - .send_pull(is_final, &key, pull_id, &max_samples); - } - - ZenohBody::LinkStateList(LinkStateList { .. }) => {} + NetworkBody::Declare(m) => self.primitives.send_declare(m), + NetworkBody::Push(m) => self.primitives.send_push(m), + NetworkBody::Request(m) => self.primitives.send_request(m), + NetworkBody::Response(m) => self.primitives.send_response(m), + NetworkBody::ResponseFinal(m) => self.primitives.send_response_final(m), + NetworkBody::OAM(_m) => (), } Ok(()) diff --git a/io/zenoh-transport/src/primitives/mod.rs b/io/zenoh-transport/src/primitives/mod.rs index 1c99b0a5fd..b79682790f 100644 --- a/io/zenoh-transport/src/primitives/mod.rs +++ b/io/zenoh-transport/src/primitives/mod.rs @@ -16,78 +16,18 @@ mod mux; pub use demux::*; pub use mux::*; -use zenoh_buffers::ZBuf; -use zenoh_protocol::{ - core::{ - Channel, CongestionControl, ConsolidationMode, QueryTarget, QueryableInfo, SubInfo, - WireExpr, ZInt, ZenohId, - }, - zenoh::{DataInfo, QueryBody, RoutingContext}, -}; +use zenoh_protocol::network::{Declare, Push, Request, Response, ResponseFinal}; pub trait Primitives: Send + Sync { - fn decl_resource(&self, expr_id: ZInt, key_expr: &WireExpr); - fn forget_resource(&self, expr_id: ZInt); + fn send_declare(&self, msg: Declare); - fn decl_publisher(&self, key_expr: &WireExpr, routing_context: Option); - fn forget_publisher(&self, key_expr: &WireExpr, routing_context: Option); + fn send_push(&self, msg: Push); - fn decl_subscriber( - &self, - key_expr: &WireExpr, - sub_info: &SubInfo, - routing_context: Option, - ); - fn forget_subscriber(&self, key_expr: &WireExpr, routing_context: Option); + fn send_request(&self, msg: Request); - fn decl_queryable( - &self, - key_expr: &WireExpr, - qabl_info: &QueryableInfo, - routing_context: Option, - ); - fn forget_queryable(&self, key_expr: &WireExpr, routing_context: Option); + fn send_response(&self, msg: Response); - fn send_data( - &self, - key_expr: &WireExpr, - payload: ZBuf, - channel: Channel, - cogestion_control: CongestionControl, - data_info: Option, - routing_context: Option, - ); - - #[allow(clippy::too_many_arguments)] - fn send_query( - &self, - key_expr: &WireExpr, - parameters: &str, - qid: ZInt, - target: QueryTarget, - consolidation: ConsolidationMode, - body: Option, - routing_context: Option, - ); - - fn send_reply_data( - &self, - qid: ZInt, - replier_id: ZenohId, - key_expr: WireExpr, - info: Option, - payload: ZBuf, - ); - - fn send_reply_final(&self, qid: ZInt); - - fn send_pull( - &self, - is_final: bool, - key_expr: &WireExpr, - pull_id: ZInt, - max_samples: &Option, - ); + fn send_response_final(&self, msg: ResponseFinal); fn send_close(&self); } @@ -102,69 +42,15 @@ impl DummyPrimitives { } impl Primitives for DummyPrimitives { - fn decl_resource(&self, _expr_id: ZInt, _key_expr: &WireExpr) {} - fn forget_resource(&self, _expr_id: ZInt) {} + fn send_declare(&self, _msg: Declare) {} - fn decl_publisher(&self, _key_expr: &WireExpr, _routing_context: Option) {} - fn forget_publisher(&self, _key_expr: &WireExpr, _routing_context: Option) {} + fn send_push(&self, _msg: Push) {} - fn decl_subscriber( - &self, - _key_expr: &WireExpr, - _sub_info: &SubInfo, - _routing_context: Option, - ) { - } - fn forget_subscriber(&self, _key_expr: &WireExpr, _routing_context: Option) {} + fn send_request(&self, _msg: Request) {} - fn decl_queryable( - &self, - _key_expr: &WireExpr, - _qable_info: &QueryableInfo, - _routing_context: Option, - ) { - } - fn forget_queryable(&self, _key_expr: &WireExpr, _routing_context: Option) {} + fn send_response(&self, _msg: Response) {} - fn send_data( - &self, - _key_expr: &WireExpr, - _payload: ZBuf, - _channel: Channel, - _cogestion_control: CongestionControl, - _info: Option, - _routing_context: Option, - ) { - } - fn send_query( - &self, - _key_expr: &WireExpr, - _parameters: &str, - _qid: ZInt, - _target: QueryTarget, - _consolidation: ConsolidationMode, - _body: Option, - _routing_context: Option, - ) { - } - fn send_reply_data( - &self, - _qid: ZInt, - _replier_id: ZenohId, - _key_expr: WireExpr, - _info: Option, - _payload: ZBuf, - ) { - } - fn send_reply_final(&self, _qid: ZInt) {} - fn send_pull( - &self, - _is_final: bool, - _key_expr: &WireExpr, - _pull_id: ZInt, - _max_samples: &Option, - ) { - } + fn send_response_final(&self, _msg: ResponseFinal) {} fn send_close(&self) {} } diff --git a/io/zenoh-transport/src/primitives/mux.rs b/io/zenoh-transport/src/primitives/mux.rs index 335303c92a..8783b8ff40 100644 --- a/io/zenoh-transport/src/primitives/mux.rs +++ b/io/zenoh-transport/src/primitives/mux.rs @@ -13,17 +13,8 @@ // use super::super::{TransportMulticast, TransportUnicast}; use super::Primitives; -use zenoh_buffers::ZBuf; -use zenoh_protocol::{ - core::{ - Channel, CongestionControl, ConsolidationMode, QueryTarget, QueryableInfo, SubInfo, - WireExpr, ZInt, ZenohId, - }, - zenoh::{ - zmsg, DataInfo, Declaration, ForgetPublisher, ForgetQueryable, ForgetResource, - ForgetSubscriber, Publisher, QueryBody, Queryable, ReplierInfo, ReplyContext, Resource, - RoutingContext, Subscriber, ZenohMessage, - }, +use zenoh_protocol::network::{ + Declare, NetworkBody, NetworkMessage, Push, Request, Response, ResponseFinal, }; pub struct Mux { @@ -37,188 +28,44 @@ impl Mux { } impl Primitives for Mux { - fn decl_resource(&self, expr_id: ZInt, key_expr: &WireExpr) { - let d = Declaration::Resource(Resource { - expr_id, - key: key_expr.to_owned(), + fn send_declare(&self, msg: Declare) { + let _ = self.handler.schedule(NetworkMessage { + body: NetworkBody::Declare(msg), + #[cfg(feature = "stats")] + size: None, }); - let decls = vec![d]; - let _ = self - .handler - .handle_message(ZenohMessage::make_declare(decls, None, None)); } - fn forget_resource(&self, expr_id: ZInt) { - let d = Declaration::ForgetResource(ForgetResource { expr_id }); - let decls = vec![d]; - let _ = self - .handler - .handle_message(ZenohMessage::make_declare(decls, None, None)); - } - - fn decl_subscriber( - &self, - key_expr: &WireExpr, - sub_info: &SubInfo, - routing_context: Option, - ) { - let d = Declaration::Subscriber(Subscriber { - key: key_expr.to_owned(), - info: sub_info.clone(), - }); - let decls = vec![d]; - let _ = - self.handler - .handle_message(ZenohMessage::make_declare(decls, routing_context, None)); - } - - fn forget_subscriber(&self, key_expr: &WireExpr, routing_context: Option) { - let d = Declaration::ForgetSubscriber(ForgetSubscriber { - key: key_expr.to_owned(), + fn send_push(&self, msg: Push) { + let _ = self.handler.schedule(NetworkMessage { + body: NetworkBody::Push(msg), + #[cfg(feature = "stats")] + size: None, }); - let decls = vec![d]; - let _ = - self.handler - .handle_message(ZenohMessage::make_declare(decls, routing_context, None)); } - fn decl_publisher(&self, key_expr: &WireExpr, routing_context: Option) { - let d = Declaration::Publisher(Publisher { - key: key_expr.to_owned(), + fn send_request(&self, msg: Request) { + let _ = self.handler.schedule(NetworkMessage { + body: NetworkBody::Request(msg), + #[cfg(feature = "stats")] + size: None, }); - let decls = vec![d]; - let _ = - self.handler - .handle_message(ZenohMessage::make_declare(decls, routing_context, None)); } - fn forget_publisher(&self, key_expr: &WireExpr, routing_context: Option) { - let d = Declaration::ForgetPublisher(ForgetPublisher { - key: key_expr.to_owned(), + fn send_response(&self, msg: Response) { + let _ = self.handler.schedule(NetworkMessage { + body: NetworkBody::Response(msg), + #[cfg(feature = "stats")] + size: None, }); - let decls = vec![d]; - let _ = - self.handler - .handle_message(ZenohMessage::make_declare(decls, routing_context, None)); } - fn decl_queryable( - &self, - key_expr: &WireExpr, - qabl_info: &QueryableInfo, - routing_context: Option, - ) { - let d = Declaration::Queryable(Queryable { - key: key_expr.to_owned(), - info: qabl_info.clone(), + fn send_response_final(&self, msg: ResponseFinal) { + let _ = self.handler.schedule(NetworkMessage { + body: NetworkBody::ResponseFinal(msg), + #[cfg(feature = "stats")] + size: None, }); - let decls = vec![d]; - let _ = - self.handler - .handle_message(ZenohMessage::make_declare(decls, routing_context, None)); - } - - fn forget_queryable(&self, key_expr: &WireExpr, routing_context: Option) { - let d = Declaration::ForgetQueryable(ForgetQueryable { - key: key_expr.to_owned(), - }); - let decls = vec![d]; - let _ = - self.handler - .handle_message(ZenohMessage::make_declare(decls, routing_context, None)); - } - - fn send_data( - &self, - key_expr: &WireExpr, - payload: ZBuf, - channel: Channel, - cogestion_control: CongestionControl, - data_info: Option, - routing_context: Option, - ) { - let _ = self.handler.handle_message(ZenohMessage::make_data( - key_expr.to_owned(), - payload, - channel, - cogestion_control, - data_info, - routing_context, - None, - None, - )); - } - - fn send_query( - &self, - key_expr: &WireExpr, - parameters: &str, - qid: ZInt, - target: QueryTarget, - consolidation: ConsolidationMode, - body: Option, - routing_context: Option, - ) { - let target_opt = if target == QueryTarget::default() { - None - } else { - Some(target) - }; - let _ = self.handler.handle_message(ZenohMessage::make_query( - key_expr.to_owned(), - parameters.to_owned(), - qid, - target_opt, - consolidation, - body, - routing_context, - None, - )); - } - - fn send_reply_data( - &self, - qid: ZInt, - replier_id: ZenohId, - key_expr: WireExpr, - data_info: Option, - payload: ZBuf, - ) { - let _ = self.handler.handle_message(ZenohMessage::make_data( - key_expr.to_owned(), - payload, - zmsg::default_channel::REPLY, - zmsg::default_congestion_control::REPLY, - data_info, - None, - Some(ReplyContext::new(qid, Some(ReplierInfo { id: replier_id }))), - None, - )); - } - - fn send_reply_final(&self, qid: ZInt) { - let _ = self.handler.handle_message(ZenohMessage::make_unit( - zmsg::default_channel::REPLY, - zmsg::default_congestion_control::REPLY, - Some(ReplyContext::new(qid, None)), - None, - )); - } - - fn send_pull( - &self, - is_final: bool, - key_expr: &WireExpr, - pull_id: ZInt, - max_samples: &Option, - ) { - let _ = self.handler.handle_message(ZenohMessage::make_pull( - is_final, - key_expr.to_owned(), - pull_id, - *max_samples, - None, - )); } fn send_close(&self) { @@ -237,188 +84,44 @@ impl McastMux { } impl Primitives for McastMux { - fn decl_resource(&self, expr_id: ZInt, key_expr: &WireExpr) { - let d = Declaration::Resource(Resource { - expr_id, - key: key_expr.to_owned(), + fn send_declare(&self, msg: Declare) { + let _ = self.handler.handle_message(NetworkMessage { + body: NetworkBody::Declare(msg), + #[cfg(feature = "stats")] + size: None, }); - let decls = vec![d]; - let _ = self - .handler - .handle_message(ZenohMessage::make_declare(decls, None, None)); } - fn forget_resource(&self, expr_id: ZInt) { - let d = Declaration::ForgetResource(ForgetResource { expr_id }); - let decls = vec![d]; - let _ = self - .handler - .handle_message(ZenohMessage::make_declare(decls, None, None)); - } - - fn decl_subscriber( - &self, - key_expr: &WireExpr, - sub_info: &SubInfo, - routing_context: Option, - ) { - let d = Declaration::Subscriber(Subscriber { - key: key_expr.to_owned(), - info: sub_info.clone(), - }); - let decls = vec![d]; - let _ = - self.handler - .handle_message(ZenohMessage::make_declare(decls, routing_context, None)); - } - - fn forget_subscriber(&self, key_expr: &WireExpr, routing_context: Option) { - let d = Declaration::ForgetSubscriber(ForgetSubscriber { - key: key_expr.to_owned(), + fn send_push(&self, msg: Push) { + let _ = self.handler.handle_message(NetworkMessage { + body: NetworkBody::Push(msg), + #[cfg(feature = "stats")] + size: None, }); - let decls = vec![d]; - let _ = - self.handler - .handle_message(ZenohMessage::make_declare(decls, routing_context, None)); } - fn decl_publisher(&self, key_expr: &WireExpr, routing_context: Option) { - let d = Declaration::Publisher(Publisher { - key: key_expr.to_owned(), + fn send_request(&self, msg: Request) { + let _ = self.handler.handle_message(NetworkMessage { + body: NetworkBody::Request(msg), + #[cfg(feature = "stats")] + size: None, }); - let decls = vec![d]; - let _ = - self.handler - .handle_message(ZenohMessage::make_declare(decls, routing_context, None)); } - fn forget_publisher(&self, key_expr: &WireExpr, routing_context: Option) { - let d = Declaration::ForgetPublisher(ForgetPublisher { - key: key_expr.to_owned(), + fn send_response(&self, msg: Response) { + let _ = self.handler.handle_message(NetworkMessage { + body: NetworkBody::Response(msg), + #[cfg(feature = "stats")] + size: None, }); - let decls = vec![d]; - let _ = - self.handler - .handle_message(ZenohMessage::make_declare(decls, routing_context, None)); } - fn decl_queryable( - &self, - key_expr: &WireExpr, - qabl_info: &QueryableInfo, - routing_context: Option, - ) { - let d = Declaration::Queryable(Queryable { - key: key_expr.to_owned(), - info: qabl_info.clone(), + fn send_response_final(&self, msg: ResponseFinal) { + let _ = self.handler.handle_message(NetworkMessage { + body: NetworkBody::ResponseFinal(msg), + #[cfg(feature = "stats")] + size: None, }); - let decls = vec![d]; - let _ = - self.handler - .handle_message(ZenohMessage::make_declare(decls, routing_context, None)); - } - - fn forget_queryable(&self, key_expr: &WireExpr, routing_context: Option) { - let d = Declaration::ForgetQueryable(ForgetQueryable { - key: key_expr.to_owned(), - }); - let decls = vec![d]; - let _ = - self.handler - .handle_message(ZenohMessage::make_declare(decls, routing_context, None)); - } - - fn send_data( - &self, - key_expr: &WireExpr, - payload: ZBuf, - channel: Channel, - cogestion_control: CongestionControl, - data_info: Option, - routing_context: Option, - ) { - let _ = self.handler.handle_message(ZenohMessage::make_data( - key_expr.to_owned(), - payload, - channel, - cogestion_control, - data_info, - routing_context, - None, - None, - )); - } - - fn send_query( - &self, - key_expr: &WireExpr, - parameters: &str, - qid: ZInt, - target: QueryTarget, - consolidation: ConsolidationMode, - body: Option, - routing_context: Option, - ) { - let target_opt = if target == QueryTarget::default() { - None - } else { - Some(target) - }; - let _ = self.handler.handle_message(ZenohMessage::make_query( - key_expr.to_owned(), - parameters.to_owned(), - qid, - target_opt, - consolidation, - body, - routing_context, - None, - )); - } - - fn send_reply_data( - &self, - qid: ZInt, - replier_id: ZenohId, - key_expr: WireExpr, - data_info: Option, - payload: ZBuf, - ) { - let _ = self.handler.handle_message(ZenohMessage::make_data( - key_expr.to_owned(), - payload, - zmsg::default_channel::REPLY, - zmsg::default_congestion_control::REPLY, - data_info, - None, - Some(ReplyContext::new(qid, Some(ReplierInfo { id: replier_id }))), - None, - )); - } - - fn send_reply_final(&self, qid: ZInt) { - let _ = self.handler.handle_message(ZenohMessage::make_unit( - zmsg::default_channel::REPLY, - zmsg::default_congestion_control::REPLY, - Some(ReplyContext::new(qid, None)), - None, - )); - } - - fn send_pull( - &self, - is_final: bool, - key_expr: &WireExpr, - pull_id: ZInt, - max_samples: &Option, - ) { - let _ = self.handler.handle_message(ZenohMessage::make_pull( - is_final, - key_expr.to_owned(), - pull_id, - *max_samples, - None, - )); } fn send_close(&self) { diff --git a/io/zenoh-transport/src/shm.rs b/io/zenoh-transport/src/shm.rs index 1f4247a5c2..04a8f502c4 100644 --- a/io/zenoh-transport/src/shm.rs +++ b/io/zenoh-transport/src/shm.rs @@ -11,182 +11,254 @@ // Contributors: // ZettaScale Zenoh Team, // -use std::{any::TypeId, sync::RwLock}; -use zenoh_buffers::{ZBuf, ZSlice}; -use zenoh_core::{zread, zwrite}; -use zenoh_protocol::zenoh::*; -use zenoh_result::ZResult; -use zenoh_shm::{ - SharedMemoryBuf, SharedMemoryBufInfo, SharedMemoryBufInfoSerialized, SharedMemoryReader, +use async_std::{sync::RwLock, task}; +use zenoh_buffers::{reader::HasReader, writer::HasWriter, ZBuf, ZSlice, ZSliceKind}; +use zenoh_codec::{RCodec, WCodec, Zenoh080}; +use zenoh_core::{zasyncread, zasyncwrite, zerror}; +use zenoh_protocol::{ + network::{NetworkBody, NetworkMessage, Push, Request, Response}, + zenoh::{ + err::{ext::ErrBodyType, Err}, + ext::ShmType, + query::{ext::QueryBodyType, Query}, + PushBody, Put, Reply, RequestBody, ResponseBody, + }, }; +use zenoh_result::ZResult; +use zenoh_shm::{SharedMemoryBuf, SharedMemoryBufInfo, SharedMemoryReader}; + +// Traits +trait MapShm { + fn map_to_shminfo(&mut self) -> ZResult; + fn map_to_shmbuf(&mut self, shmr: &RwLock) -> ZResult; +} -macro_rules! unset_sliced { - ($msg:expr, $data_info:expr) => { - // Set the right data info SHM parameters - if let Some(di) = $data_info { - di.sliced = false; - if di == &DataInfo::default() { - *$data_info = None; - } +macro_rules! map_to_shminfo { + ($zbuf:expr, $ext_shm:expr) => {{ + let res = map_zbuf_to_shminfo($zbuf)?; + if res { + *$ext_shm = Some(ShmType::new()); } - }; + Ok(res) + }}; } -macro_rules! set_sliced { - ($msg:expr, $data_info:expr) => { - match $data_info { - Some(di) => { - // Just update the is_shm field. This field can be - // then used at receiver side to identify that the - // actual content is stored in shared memory - di.sliced = true; - } - None => { - // Create the DataInfo content - let di = DataInfo { - sliced: true, - ..Default::default() - }; - *$data_info = Some(di); - } +macro_rules! map_to_shmbuf { + ($zbuf:expr, $ext_shm:expr, $shmr:expr) => {{ + if $ext_shm.is_some() { + *$ext_shm = None; + map_zbuf_to_shmbuf($zbuf, $shmr) + } else { + Ok(false) } - }; + }}; } -pub fn map_zslice_to_shmbuf( - zslice: &mut ZSlice, - shmr: &RwLock, -) -> ZResult { - let mut res = false; +// Impl - Put +impl MapShm for Put { + fn map_to_shminfo(&mut self) -> ZResult { + let Self { + payload, ext_shm, .. + } = self; + map_to_shminfo!(payload, ext_shm) + } - let ZSlice { buf, .. } = zslice; - if buf.as_any().type_id() == TypeId::of::() { - // Deserialize the shmb info into shm buff - let shmbinfo = SharedMemoryBufInfo::deserialize(buf.as_slice())?; - - // First, try in read mode allowing concurrenct lookups - let r_guard = zread!(shmr); - let smb = r_guard.try_read_shmbuf(&shmbinfo).or_else(|_| { - // Next, try in write mode to eventual link the remote shm - drop(r_guard); - let mut w_guard = zwrite!(shmr); - w_guard.read_shmbuf(&shmbinfo) - })?; - - // Replace the content of the slice - let zs: ZSlice = smb.into(); - *zslice = zs; - - res = true; + fn map_to_shmbuf(&mut self, shmr: &RwLock) -> ZResult { + let Self { + payload, ext_shm, .. + } = self; + map_to_shmbuf!(payload, ext_shm, shmr) } - Ok(res) } -pub fn map_zbuf_to_shmbuf(zbuf: &mut ZBuf, shmr: &RwLock) -> ZResult { - let mut res = false; - for zs in zbuf.zslices_mut() { - res |= map_zslice_to_shmbuf(zs, shmr)?; +// Impl - Query +impl MapShm for Query { + fn map_to_shminfo(&mut self) -> ZResult { + if let Self { + ext_body: Some(QueryBodyType { + payload, ext_shm, .. + }), + .. + } = self + { + map_to_shminfo!(payload, ext_shm) + } else { + Ok(false) + } + } + + fn map_to_shmbuf(&mut self, shmr: &RwLock) -> ZResult { + if let Self { + ext_body: Some(QueryBodyType { + payload, ext_shm, .. + }), + .. + } = self + { + map_to_shmbuf!(payload, ext_shm, shmr) + } else { + Ok(false) + } } - Ok(res) } -pub fn map_zslice_to_shminfo(zslice: &mut ZSlice) -> ZResult { - let mut res = false; - let ZSlice { buf, .. } = zslice; - if let Some(shmb) = buf.as_any().downcast_ref::() { - // Serialize the shmb info - let info: SharedMemoryBufInfoSerialized = shmb.info.serialize()?.into(); - // Increase the reference count so to keep the SharedMemoryBuf valid - shmb.inc_ref_count(); - // Replace the content of the slice - let zs: ZSlice = info.into(); - *zslice = zs; - - res = true; +// Impl - Reply +impl MapShm for Reply { + fn map_to_shminfo(&mut self) -> ZResult { + let Self { + payload, ext_shm, .. + } = self; + map_to_shminfo!(payload, ext_shm) + } + + fn map_to_shmbuf(&mut self, shmr: &RwLock) -> ZResult { + let Self { + payload, ext_shm, .. + } = self; + map_to_shmbuf!(payload, ext_shm, shmr) + } +} + +// Impl - Err +impl MapShm for Err { + fn map_to_shminfo(&mut self) -> ZResult { + if let Self { + ext_body: Some(ErrBodyType { + payload, ext_shm, .. + }), + .. + } = self + { + map_to_shminfo!(payload, ext_shm) + } else { + Ok(false) + } + } + + fn map_to_shmbuf(&mut self, shmr: &RwLock) -> ZResult { + if let Self { + ext_body: Some(ErrBodyType { + payload, ext_shm, .. + }), + .. + } = self + { + map_to_shmbuf!(payload, ext_shm, shmr) + } else { + Ok(false) + } } - Ok(res) } +// ShmBuf -> ShmInfo +pub fn map_zmsg_to_shminfo(msg: &mut NetworkMessage) -> ZResult { + match &mut msg.body { + NetworkBody::Push(Push { payload, .. }) => match payload { + PushBody::Put(b) => b.map_to_shminfo(), + PushBody::Del(_) => Ok(false), + }, + NetworkBody::Request(Request { payload, .. }) => match payload { + RequestBody::Query(b) => b.map_to_shminfo(), + RequestBody::Put(b) => b.map_to_shminfo(), + RequestBody::Del(_) | RequestBody::Pull(_) => Ok(false), + }, + NetworkBody::Response(Response { payload, .. }) => match payload { + ResponseBody::Reply(b) => b.map_to_shminfo(), + ResponseBody::Put(b) => b.map_to_shminfo(), + ResponseBody::Err(b) => b.map_to_shminfo(), + ResponseBody::Ack(_) => Ok(false), + }, + NetworkBody::ResponseFinal(_) | NetworkBody::Declare(_) | NetworkBody::OAM(_) => Ok(false), + } +} + +// Mapping pub fn map_zbuf_to_shminfo(zbuf: &mut ZBuf) -> ZResult { let mut res = false; for zs in zbuf.zslices_mut() { - res |= map_zslice_to_shminfo(zs)?; + if let Some(shmb) = zs.downcast_ref::() { + *zs = map_zslice_to_shminfo(shmb)?; + res = true; + } } Ok(res) } +#[cold] +#[inline(never)] +pub fn map_zslice_to_shminfo(shmb: &SharedMemoryBuf) -> ZResult { + // Serialize the shmb info + let codec = Zenoh080::new(); + let mut info = vec![]; + let mut writer = info.writer(); + codec + .write(&mut writer, &shmb.info) + .map_err(|e| zerror!("{:?}", e))?; + // Increase the reference count so to keep the SharedMemoryBuf valid + shmb.inc_ref_count(); + // Replace the content of the slice + let mut zslice: ZSlice = info.into(); + zslice.kind = ZSliceKind::ShmPtr; + Ok(zslice) +} + +// ShmInfo -> ShmBuf pub fn map_zmsg_to_shmbuf( - msg: &mut ZenohMessage, + msg: &mut NetworkMessage, shmr: &RwLock, ) -> ZResult { - let mut res = false; - - if let Some(attachment) = msg.attachment.as_mut() { - res = map_zbuf_to_shmbuf(&mut attachment.buffer, shmr)?; + match &mut msg.body { + NetworkBody::Push(Push { payload, .. }) => match payload { + PushBody::Put(b) => b.map_to_shmbuf(shmr), + PushBody::Del(_) => Ok(false), + }, + NetworkBody::Request(Request { payload, .. }) => match payload { + RequestBody::Query(b) => b.map_to_shmbuf(shmr), + RequestBody::Put(b) => b.map_to_shmbuf(shmr), + RequestBody::Del(_) | RequestBody::Pull(_) => Ok(false), + }, + NetworkBody::Response(Response { payload, .. }) => match payload { + ResponseBody::Put(b) => b.map_to_shmbuf(shmr), + ResponseBody::Err(b) => b.map_to_shmbuf(shmr), + ResponseBody::Reply(b) => b.map_to_shmbuf(shmr), + ResponseBody::Ack(_) => Ok(false), + }, + NetworkBody::ResponseFinal(_) | NetworkBody::Declare(_) | NetworkBody::OAM(_) => Ok(false), } - - if let ZenohBody::Data(Data { - payload, data_info, .. - }) = &mut msg.body - { - res |= map_zbuf_to_shmbuf(payload, shmr)?; - unset_sliced!(msg, data_info); - } else if let ZenohBody::Query(Query { - body: Some(body), .. - }) = &mut msg.body - { - res |= map_zbuf_to_shmbuf(&mut body.payload, shmr)?; - body.data_info.sliced = false; - } - - Ok(res) } -pub fn map_zmsg_to_shminfo(msg: &mut ZenohMessage) -> ZResult { +// Mapping +pub fn map_zbuf_to_shmbuf(zbuf: &mut ZBuf, shmr: &RwLock) -> ZResult { let mut res = false; - - if let Some(attachment) = msg.attachment.as_mut() { - res = map_zbuf_to_shminfo(&mut attachment.buffer)?; - } - - if let ZenohBody::Data(Data { - payload, data_info, .. - }) = &mut msg.body - { - res |= map_zbuf_to_shminfo(payload)?; - set_sliced!(msg, data_info); - } else if let ZenohBody::Query(Query { - body: Some(body), .. - }) = &mut msg.body - { - res |= map_zbuf_to_shminfo(&mut body.payload)?; - body.data_info.sliced = true; + for zs in zbuf.zslices_mut().filter(|x| x.kind == ZSliceKind::ShmPtr) { + res |= map_zslice_to_shmbuf(zs, shmr)?; } - Ok(res) } -// Unused for the time being -// -// #[cfg(feature = "shared-memory")] -// impl TransportMessage { -// pub(crate) fn map_to_shmbuf(&mut self, shmr: Arc>) -> ZResult { -// let mut res = false; - -// if let Some(attachment) = self.attachment.as_mut() { -// res = attachment.buffer.map_to_shmbuf(shmr)?; -// } +#[cold] +#[inline(never)] +pub fn map_zslice_to_shmbuf( + zslice: &mut ZSlice, + shmr: &RwLock, +) -> ZResult { + // Deserialize the shmb info into shm buff + let codec = Zenoh080::new(); + let mut reader = zslice.reader(); -// Ok(res) -// } + let shmbinfo: SharedMemoryBufInfo = codec.read(&mut reader).map_err(|e| zerror!("{:?}", e))?; -// pub(crate) fn map_to_shminfo(&mut self) -> ZResult { -// let mut res = false; + // First, try in read mode allowing concurrenct lookups + let r_guard = task::block_on(async { zasyncread!(shmr) }); + let smb = r_guard.try_read_shmbuf(&shmbinfo).or_else(|_| { + drop(r_guard); + let mut w_guard = task::block_on(async { zasyncwrite!(shmr) }); + w_guard.read_shmbuf(&shmbinfo) + })?; -// if let Some(attachment) = self.attachment.as_mut() { -// res = attachment.buffer.map_to_shminfo()?; -// } + // Replace the content of the slice + let zs: ZSlice = smb.into(); + *zslice = zs; -// Ok(res) -// } -// } + Ok(true) +} diff --git a/io/zenoh-transport/src/unicast/establishment/accept.rs b/io/zenoh-transport/src/unicast/establishment/accept.rs new file mode 100644 index 0000000000..01b5f4af61 --- /dev/null +++ b/io/zenoh-transport/src/unicast/establishment/accept.rs @@ -0,0 +1,633 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +#[cfg(feature = "shared-memory")] +use crate::unicast::shared_memory_unicast::Challenge; +use crate::{ + unicast::establishment::{ + close_link, compute_sn, ext, finalize_transport, AcceptFsm, Cookie, InputFinalize, + Zenoh080Cookie, + }, + TransportConfigUnicast, TransportManager, +}; +use async_std::sync::Mutex; +use async_trait::async_trait; +use rand::Rng; +use std::time::Duration; +use zenoh_buffers::{reader::HasReader, writer::HasWriter, ZSlice}; +use zenoh_codec::{RCodec, WCodec, Zenoh080}; +use zenoh_core::{zasynclock, zcondfeat, zerror}; +use zenoh_crypto::{BlockCipher, PseudoRng}; +use zenoh_link::{LinkUnicast, LinkUnicastDirection}; +use zenoh_protocol::{ + core::{Field, Resolution, WhatAmI, ZenohId}, + transport::{ + batch_size, + close::{self, Close}, + BatchSize, InitAck, OpenAck, TransportBody, TransportMessage, TransportSn, + }, +}; +use zenoh_result::ZResult; + +pub(super) type AcceptError = (zenoh_result::Error, Option); + +struct StateZenoh { + batch_size: BatchSize, + resolution: Resolution, +} + +struct State { + zenoh: StateZenoh, + ext_qos: ext::qos::StateAccept, + #[cfg(feature = "transport_multilink")] + ext_mlink: ext::multilink::StateAccept, + #[cfg(feature = "shared-memory")] + ext_shm: ext::shm::StateAccept, + #[cfg(feature = "transport_auth")] + ext_auth: ext::auth::StateAccept, +} + +// InitSyn +struct RecvInitSynIn { + mine_version: u8, +} +struct RecvInitSynOut { + other_zid: ZenohId, + other_whatami: WhatAmI, + #[cfg(feature = "shared-memory")] + ext_shm: Challenge, +} + +// InitAck +struct SendInitAckIn { + mine_version: u8, + mine_zid: ZenohId, + mine_whatami: WhatAmI, + other_zid: ZenohId, + other_whatami: WhatAmI, + #[cfg(feature = "shared-memory")] + ext_shm: Challenge, +} +struct SendInitAckOut { + cookie_nonce: u64, +} + +// OpenSyn +struct RecvOpenSynIn { + cookie_nonce: u64, +} +struct RecvOpenSynOut { + other_zid: ZenohId, + other_whatami: WhatAmI, + other_lease: Duration, + other_initial_sn: TransportSn, +} + +// OpenAck +struct SendOpenAckIn { + mine_zid: ZenohId, + mine_lease: Duration, + other_zid: ZenohId, +} +struct SendOpenAckOut { + open_ack: OpenAck, +} + +// Fsm +struct AcceptLink<'a> { + link: &'a LinkUnicast, + prng: &'a Mutex, + cipher: &'a BlockCipher, + ext_qos: ext::qos::QoSFsm<'a>, + #[cfg(feature = "transport_multilink")] + ext_mlink: ext::multilink::MultiLinkFsm<'a>, + #[cfg(feature = "shared-memory")] + ext_shm: ext::shm::ShmFsm<'a>, + #[cfg(feature = "transport_auth")] + ext_auth: ext::auth::AuthFsm<'a>, +} + +#[async_trait] +impl<'a> AcceptFsm for AcceptLink<'a> { + type Error = AcceptError; + + type RecvInitSynIn = (&'a mut State, RecvInitSynIn); + type RecvInitSynOut = RecvInitSynOut; + async fn recv_init_syn( + &self, + input: Self::RecvInitSynIn, + ) -> Result { + let (state, input) = input; + + let msg = self + .link + .recv() + .await + .map_err(|e| (e, Some(close::reason::INVALID)))?; + + let init_syn = match msg.body { + TransportBody::InitSyn(init_syn) => init_syn, + _ => { + let e = zerror!( + "Received invalid message instead of an InitSyn on {}: {:?}", + self.link, + msg.body + ); + return Err((e.into(), Some(close::reason::INVALID))); + } + }; + + // Check if the version is supported + if init_syn.version != input.mine_version { + let e = zerror!( + "Rejecting InitSyn on {} because of unsupported Zenoh version from peer: {}", + self.link, + init_syn.zid + ); + return Err((e.into(), Some(close::reason::INVALID))); + } + + // Compute the minimum SN resolution + state.zenoh.resolution = { + let mut res = Resolution::default(); + + // Frame SN + let i_fsn_res = init_syn.resolution.get(Field::FrameSN); + let m_fsn_res = state.zenoh.resolution.get(Field::FrameSN); + res.set(Field::FrameSN, i_fsn_res.min(m_fsn_res)); + + // Request ID + let i_rid_res = init_syn.resolution.get(Field::RequestID); + let m_rid_res = state.zenoh.resolution.get(Field::RequestID); + res.set(Field::RequestID, i_rid_res.min(m_rid_res)); + + res + }; + + // Compute the minimum batch size + state.zenoh.batch_size = state + .zenoh + .batch_size + .min(init_syn.batch_size) + .min(batch_size::UNICAST); + + // Extension QoS + self.ext_qos + .recv_init_syn((&mut state.ext_qos, init_syn.ext_qos)) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?; + + // Extension Shm + #[cfg(feature = "shared-memory")] + let ext_shm = self + .ext_shm + .recv_init_syn((&mut state.ext_shm, init_syn.ext_shm)) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?; + + // Extension Auth + #[cfg(feature = "transport_auth")] + self.ext_auth + .recv_init_syn((&mut state.ext_auth, init_syn.ext_auth)) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?; + + // Extension MultiLink + #[cfg(feature = "transport_multilink")] + self.ext_mlink + .recv_init_syn((&mut state.ext_mlink, init_syn.ext_mlink)) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?; + + let output = RecvInitSynOut { + other_zid: init_syn.zid, + other_whatami: init_syn.whatami, + #[cfg(feature = "shared-memory")] + ext_shm, + }; + Ok(output) + } + + type SendInitAckIn = (State, SendInitAckIn); + type SendInitAckOut = SendInitAckOut; + async fn send_init_ack( + &self, + input: Self::SendInitAckIn, + ) -> Result { + #[allow(unused_mut)] // Required for "shared-memory" feature + let (mut state, input) = input; + + // Extension QoS + let ext_qos = self + .ext_qos + .send_init_ack(&state.ext_qos) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?; + + // Extension Shm + let ext_shm = zcondfeat!( + "shared-memory", + self.ext_shm + .send_init_ack((&mut state.ext_shm, input.ext_shm)) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?, + None + ); + + // Extension Auth + let ext_auth = zcondfeat!( + "transport_auth", + self.ext_auth + .send_init_ack(&state.ext_auth) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?, + None + ); + + // Extension MultiLink + let ext_mlink = zcondfeat!( + "transport_multilink", + self.ext_mlink + .send_init_ack(&state.ext_mlink) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?, + None + ); + + // Create the cookie + let cookie_nonce: u64 = zasynclock!(self.prng).gen(); + let cookie = Cookie { + zid: input.other_zid, + whatami: input.other_whatami, + resolution: state.zenoh.resolution, + batch_size: state.zenoh.batch_size, + nonce: cookie_nonce, + ext_qos: state.ext_qos, + #[cfg(feature = "transport_multilink")] + ext_mlink: state.ext_mlink, + #[cfg(feature = "shared-memory")] + ext_shm: state.ext_shm, + #[cfg(feature = "transport_auth")] + ext_auth: state.ext_auth, + }; + + let mut encrypted = vec![]; + let mut writer = encrypted.writer(); + let mut codec = Zenoh080Cookie { + prng: &mut *zasynclock!(self.prng), + cipher: self.cipher, + codec: Zenoh080::new(), + }; + codec.write(&mut writer, &cookie).map_err(|_| { + ( + zerror!("Encoding cookie failed").into(), + Some(close::reason::INVALID), + ) + })?; + let cookie: ZSlice = encrypted.into(); + + // Send the message on the link + let message: TransportMessage = InitAck { + version: input.mine_version, + whatami: input.mine_whatami, + zid: input.mine_zid, + resolution: state.zenoh.resolution, + batch_size: state.zenoh.batch_size, + cookie, + ext_qos, + ext_shm, + ext_auth, + ext_mlink, + } + .into(); + + let _ = self + .link + .send(&message) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?; + + let output = SendInitAckOut { cookie_nonce }; + Ok(output) + } + + type RecvOpenSynIn = RecvOpenSynIn; + type RecvOpenSynOut = (State, RecvOpenSynOut); + async fn recv_open_syn( + &self, + input: Self::RecvOpenSynIn, + ) -> Result { + let msg = self + .link + .recv() + .await + .map_err(|e| (e, Some(close::reason::INVALID)))?; + + let open_syn = match msg.body { + TransportBody::OpenSyn(open_syn) => open_syn, + TransportBody::Close(Close { reason, .. }) => { + let e = zerror!( + "Received a close message (reason {}) instead of an OpenSyn on: {:?}", + close::reason_to_str(reason), + self.link, + ); + match reason { + close::reason::MAX_LINKS => log::debug!("{}", e), + _ => log::error!("{}", e), + } + return Err((e.into(), None)); + } + _ => { + let e = zerror!( + "Received invalid message instead of an OpenSyn on {}: {:?}", + self.link, + msg.body + ); + log::error!("{}", e); + return Err((e.into(), Some(close::reason::INVALID))); + } + }; + let encrypted = open_syn.cookie.to_vec(); + + // Decrypt the cookie with the cipher + let cookie: Cookie = { + let mut codec = Zenoh080Cookie { + prng: &mut *zasynclock!(self.prng), + cipher: self.cipher, + codec: Zenoh080::new(), + }; + let mut reader = encrypted.reader(); + codec.read(&mut reader).map_err(|_| { + ( + zerror!("Decoding cookie failed").into(), + Some(close::reason::INVALID), + ) + }) + }?; + + // Verify that the cookie is the one we sent + if input.cookie_nonce != cookie.nonce { + let e = zerror!("Rejecting OpenSyn on: {}. Unkwown cookie.", self.link); + return Err((e.into(), Some(close::reason::INVALID))); + } + + // Rebuild the state from the cookie + let mut state = State { + zenoh: StateZenoh { + batch_size: cookie.batch_size, + resolution: cookie.resolution, + }, + ext_qos: cookie.ext_qos, + #[cfg(feature = "transport_multilink")] + ext_mlink: cookie.ext_mlink, + #[cfg(feature = "shared-memory")] + ext_shm: cookie.ext_shm, + #[cfg(feature = "transport_auth")] + ext_auth: cookie.ext_auth, + }; + + // Extension QoS + self.ext_qos + .recv_open_syn((&mut state.ext_qos, open_syn.ext_qos)) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?; + + // Extension Shm + #[cfg(feature = "shared-memory")] + self.ext_shm + .recv_open_syn((&mut state.ext_shm, open_syn.ext_shm)) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?; + + // Extension Auth + #[cfg(feature = "transport_auth")] + self.ext_auth + .recv_open_syn((&mut state.ext_auth, open_syn.ext_auth)) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?; + + // Extension MultiLink + #[cfg(feature = "transport_multilink")] + self.ext_mlink + .recv_open_syn((&mut state.ext_mlink, open_syn.ext_mlink)) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?; + + let output = RecvOpenSynOut { + other_zid: cookie.zid, + other_whatami: cookie.whatami, + other_lease: open_syn.lease, + other_initial_sn: open_syn.initial_sn, + }; + Ok((state, output)) + } + + type SendOpenAckIn = (&'a mut State, SendOpenAckIn); + type SendOpenAckOut = SendOpenAckOut; + async fn send_open_ack( + &self, + input: Self::SendOpenAckIn, + ) -> Result { + let (state, input) = input; + + // Extension QoS + let ext_qos = self + .ext_qos + .send_open_ack(&state.ext_qos) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?; + + // Extension Shm + let ext_shm = zcondfeat!( + "shared-memory", + self.ext_shm + .send_open_ack(&mut state.ext_shm) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?, + None + ); + + // Extension Auth + let ext_auth = zcondfeat!( + "transport_auth", + self.ext_auth + .send_open_ack(&state.ext_auth) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?, + None + ); + + // Extension MultiLink + let ext_mlink = zcondfeat!( + "transport_multilink", + self.ext_mlink + .send_open_ack(&state.ext_mlink) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?, + None + ); + + // Build OpenAck message + let mine_initial_sn = compute_sn(input.mine_zid, input.other_zid, state.zenoh.resolution); + let open_ack = OpenAck { + lease: input.mine_lease, + initial_sn: mine_initial_sn, + ext_qos, + ext_shm, + ext_auth, + ext_mlink, + }; + + // Do not send the OpenAck right now since we might still incur in MAX_LINKS error + + let output = SendOpenAckOut { open_ack }; + Ok(output) + } +} + +pub(crate) async fn accept_link(link: &LinkUnicast, manager: &TransportManager) -> ZResult<()> { + let fsm = AcceptLink { + link, + prng: &manager.prng, + cipher: &manager.cipher, + ext_qos: ext::qos::QoSFsm::new(), + #[cfg(feature = "shared-memory")] + ext_shm: ext::shm::ShmFsm::new(&manager.state.unicast.shm), + #[cfg(feature = "transport_multilink")] + ext_mlink: manager.state.unicast.multilink.fsm(&manager.prng), + #[cfg(feature = "transport_auth")] + ext_auth: manager.state.unicast.authenticator.fsm(&manager.prng), + }; + + // Init handshake + macro_rules! step { + ($res:expr) => { + match $res { + Ok(output) => output, + Err((e, reason)) => { + log::debug!("{}", e); + close_link(link, reason).await; + return Err(e); + } + } + }; + } + + let iack_out = { + let mut state = State { + zenoh: StateZenoh { + batch_size: manager.config.batch_size, + resolution: manager.config.resolution, + }, + ext_qos: ext::qos::StateAccept::new(manager.config.unicast.is_qos), + #[cfg(feature = "transport_multilink")] + ext_mlink: manager + .state + .unicast + .multilink + .accept(manager.config.unicast.max_links > 1), + #[cfg(feature = "shared-memory")] + ext_shm: ext::shm::StateAccept::new(manager.config.unicast.is_shm), + #[cfg(feature = "transport_auth")] + ext_auth: manager + .state + .unicast + .authenticator + .accept(&mut *zasynclock!(manager.prng)), + }; + + // Let's scope the Init phase in such a way memory is freed by Rust + // after having sent the InitAck. The state will be recovered + // from the Cookie received in the OpenSyn. + let isyn_in = RecvInitSynIn { + mine_version: manager.config.version, + }; + let isyn_out = step!(fsm.recv_init_syn((&mut state, isyn_in)).await); + + let iack_in = SendInitAckIn { + mine_version: manager.config.version, + mine_zid: manager.config.zid, + mine_whatami: manager.config.whatami, + other_zid: isyn_out.other_zid, + other_whatami: isyn_out.other_whatami, + #[cfg(feature = "shared-memory")] + ext_shm: isyn_out.ext_shm, + }; + step!(fsm.send_init_ack((state, iack_in)).await) + }; + + // Open handshake + let osyn_in = RecvOpenSynIn { + cookie_nonce: iack_out.cookie_nonce, + }; + let (mut state, osyn_out) = step!(fsm.recv_open_syn(osyn_in).await); + + // Create the OpenAck but not send it yet + let oack_in = SendOpenAckIn { + mine_zid: manager.config.zid, + mine_lease: manager.config.unicast.lease, + other_zid: osyn_out.other_zid, + }; + let oack_out = step!(fsm.send_open_ack((&mut state, oack_in)).await); + + // Initialize the transport + let config = TransportConfigUnicast { + zid: osyn_out.other_zid, + whatami: osyn_out.other_whatami, + sn_resolution: state.zenoh.resolution.get(Field::FrameSN), + tx_initial_sn: oack_out.open_ack.initial_sn, + is_qos: state.ext_qos.is_qos(), + #[cfg(feature = "transport_multilink")] + multilink: state.ext_mlink.multilink(), + #[cfg(feature = "shared-memory")] + is_shm: state.ext_shm.is_shm(), + }; + + let transport = step!( + manager + .init_transport_unicast(config, link.clone(), LinkUnicastDirection::Inbound) + .await + ); + + // Send the open_ack on the link + step!(link + .send(&oack_out.open_ack.into()) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))); + + // Sync the RX sequence number + let _ = step!(transport + .get_inner() + .map_err(|e| (e, Some(close::reason::INVALID)))) + .sync(osyn_out.other_initial_sn) + .await; + + // Finalize the transport + let input = InputFinalize { + transport: transport.clone(), + other_lease: osyn_out.other_lease, + agreed_batch_size: state.zenoh.batch_size, + }; + step!(finalize_transport(link, manager, input) + .await + .map_err(|e| (e, Some(close::reason::INVALID)))); + + log::debug!( + "New transport link accepted from {} to {}: {}", + osyn_out.other_zid, + manager.config.zid, + link + ); + + Ok(()) +} diff --git a/io/zenoh-transport/src/unicast/establishment/accept/init_ack.rs b/io/zenoh-transport/src/unicast/establishment/accept/init_ack.rs deleted file mode 100644 index 0d65d6a7cd..0000000000 --- a/io/zenoh-transport/src/unicast/establishment/accept/init_ack.rs +++ /dev/null @@ -1,148 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use super::{init_syn, AResult}; -use crate::{ - unicast::establishment::{ - authenticator::AuthenticatedPeerLink, Cookie, EstablishmentProperties, Zenoh060Cookie, - }, - TransportManager, -}; -use rand::Rng; -use std::convert::TryFrom; -use zenoh_buffers::{writer::HasWriter, ZSlice}; -use zenoh_codec::{WCodec, Zenoh060}; -use zenoh_core::{zasynclock, zasyncread}; -use zenoh_crypto::hmac; -use zenoh_link::LinkUnicast; -use zenoh_protocol::{ - common::Attachment, - core::Property, - transport::{tmsg, TransportMessage}, -}; -use zenoh_result::zerror; - -// Send an InitAck -pub(super) struct Output { - pub(super) cookie_hash: Vec, -} - -pub(super) async fn send( - link: &LinkUnicast, - manager: &TransportManager, - auth_link: &AuthenticatedPeerLink, - mut input: init_syn::Output, -) -> AResult { - // Compute the minimum SN Resolution - let agreed_sn_resolution = manager.config.sn_resolution.min(input.sn_resolution); - - // Build the fields for the InitAck message - let whatami = manager.config.whatami; - let azid = manager.config.zid; - let sn_resolution = if agreed_sn_resolution == input.sn_resolution { - None - } else { - Some(agreed_sn_resolution) - }; - - // Create the cookie - let mut cookie = Cookie { - whatami: input.whatami, - zid: input.zid, - sn_resolution: agreed_sn_resolution, - is_qos: input.is_qos, - nonce: zasynclock!(manager.prng).gen_range(0..agreed_sn_resolution), - properties: EstablishmentProperties::new(), - }; - - // Build the attachment from the authenticators - let mut ps_attachment = EstablishmentProperties::new(); - let mut ps_cookie = EstablishmentProperties::new(); - for pa in zasyncread!(manager.state.unicast.peer_authenticator).iter() { - let (mut att, mut cke) = pa - .handle_init_syn( - auth_link, - &cookie, - input - .init_syn_properties - .remove(pa.id().into()) - .map(|x| x.value), - ) - .await - .map_err(|e| (e, Some(tmsg::close_reason::INVALID)))?; - // Add attachment property if available - if let Some(att) = att.take() { - ps_attachment - .insert(Property { - key: pa.id().into(), - value: att, - }) - .map_err(|e| (e, Some(tmsg::close_reason::UNSUPPORTED)))?; - } - // Add state in the cookie if avaiable - if let Some(cke) = cke.take() { - ps_cookie - .insert(Property { - key: pa.id().into(), - value: cke, - }) - .map_err(|e| (e, Some(tmsg::close_reason::UNSUPPORTED)))?; - } - } - cookie.properties = ps_cookie; - - let attachment: Option = if ps_attachment.is_empty() { - None - } else { - let att = Attachment::try_from(&ps_attachment) - .map_err(|e| (e, Some(tmsg::close_reason::INVALID)))?; - Some(att) - }; - - let mut encrypted = vec![]; - let mut writer = encrypted.writer(); - let mut codec = Zenoh060Cookie { - prng: &mut *zasynclock!(manager.prng), - cipher: &manager.cipher, - codec: Zenoh060::default(), - }; - codec.write(&mut writer, &cookie).map_err(|_| { - ( - zerror!("Encoding cookie failed").into(), - Some(tmsg::close_reason::INVALID), - ) - })?; - - // Compute the cookie hash - let cookie_hash = hmac::digest(&encrypted); - - // Send the cookie - let cookie: ZSlice = encrypted.into(); - let message = TransportMessage::make_init_ack( - whatami, - azid, - sn_resolution, - input.is_qos, - cookie, - attachment, - ); - - // Send the message on the link - let _ = link - .write_transport_message(&message) - .await - .map_err(|e| (e, Some(tmsg::close_reason::GENERIC)))?; - - let output = Output { cookie_hash }; - Ok(output) -} diff --git a/io/zenoh-transport/src/unicast/establishment/accept/init_syn.rs b/io/zenoh-transport/src/unicast/establishment/accept/init_syn.rs deleted file mode 100644 index c7b6ed2dd3..0000000000 --- a/io/zenoh-transport/src/unicast/establishment/accept/init_syn.rs +++ /dev/null @@ -1,111 +0,0 @@ -use std::convert::TryFrom; - -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use super::super::{AuthenticatedPeerLink, EstablishmentProperties}; -use super::AResult; -use crate::TransportManager; -use zenoh_link::LinkUnicast; -use zenoh_protocol::{ - core::{WhatAmI, ZInt, ZenohId}, - transport::{tmsg, TransportBody}, -}; -use zenoh_result::zerror; - -/*************************************/ -/* ACCEPT */ -/*************************************/ - -// Read and eventually accept an InitSyn -pub(super) struct Output { - pub(super) whatami: WhatAmI, - pub(super) zid: ZenohId, - pub(super) sn_resolution: ZInt, - pub(super) is_qos: bool, - pub(super) init_syn_properties: EstablishmentProperties, -} -pub(super) async fn recv( - link: &LinkUnicast, - manager: &TransportManager, - auth_link: &mut AuthenticatedPeerLink, -) -> AResult { - // Wait to read an InitSyn - let mut messages = link - .read_transport_message() - .await - .map_err(|e| (e, Some(tmsg::close_reason::INVALID)))?; - if messages.len() != 1 { - let e = zerror!( - "Received multiple messages instead of a single InitSyn on {}: {:?}", - link, - messages, - ); - return Err((e.into(), Some(tmsg::close_reason::INVALID))); - } - - let mut msg = messages.remove(0); - let init_syn = match msg.body { - TransportBody::InitSyn(init_syn) => init_syn, - _ => { - let e = zerror!( - "Received invalid message instead of an InitSyn on {}: {:?}", - link, - msg.body - ); - return Err((e.into(), Some(tmsg::close_reason::INVALID))); - } - }; - - // Check the peer id associate to the authenticated link - match auth_link.peer_id { - Some(zid) => { - if zid != init_syn.zid { - let e = zerror!( - "Inconsistent ZenohId in InitSyn on {}: {:?} {:?}", - link, - zid, - init_syn.zid - ); - return Err((e.into(), Some(tmsg::close_reason::INVALID))); - } - } - None => auth_link.peer_id = Some(init_syn.zid), - } - - // Check if the version is supported - if init_syn.version != manager.config.version { - let e = zerror!( - "Rejecting InitSyn on {} because of unsupported Zenoh version from peer: {}", - link, - init_syn.zid - ); - return Err((e.into(), Some(tmsg::close_reason::INVALID))); - } - - // Validate the InitSyn with the peer authenticators - let init_syn_properties: EstablishmentProperties = match msg.attachment.take() { - Some(att) => EstablishmentProperties::try_from(&att) - .map_err(|e| (e, Some(tmsg::close_reason::INVALID)))?, - None => EstablishmentProperties::new(), - }; - - let output = Output { - whatami: init_syn.whatami, - zid: init_syn.zid, - sn_resolution: init_syn.sn_resolution, - is_qos: init_syn.is_qos, - init_syn_properties, - }; - Ok(output) -} diff --git a/io/zenoh-transport/src/unicast/establishment/accept/mod.rs b/io/zenoh-transport/src/unicast/establishment/accept/mod.rs deleted file mode 100644 index c7ab9f4938..0000000000 --- a/io/zenoh-transport/src/unicast/establishment/accept/mod.rs +++ /dev/null @@ -1,126 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -mod init_ack; -mod init_syn; -mod open_ack; -mod open_syn; - -use crate::unicast::establishment::authenticator::AuthenticatedPeerLink; -use crate::unicast::establishment::{ - close_link, transport_finalize, transport_init, InputFinalize, -}; -use crate::TransportManager; -use zenoh_link::{LinkUnicast, LinkUnicastDirection}; -use zenoh_protocol::transport::tmsg; -use zenoh_result::ZResult; - -pub(super) type AError = (zenoh_result::Error, Option); -pub(super) type AResult = Result; - -pub(crate) async fn accept_link( - link: &LinkUnicast, - manager: &TransportManager, - auth_link: &mut AuthenticatedPeerLink, -) -> ZResult<()> { - // INIT handshake - macro_rules! step { - ($s: expr) => { - match $s { - Ok(output) => output, - Err((e, reason)) => { - log::error!("{}", e); - close_link(link, manager, auth_link, reason).await; - return Err(e); - } - } - }; - } - - let output = step!(init_syn::recv(link, manager, auth_link).await); - let output = step!(init_ack::send(link, manager, auth_link, output).await); - let output = step!(open_syn::recv(link, manager, auth_link, output).await); - - // Initialize the transport - let zid = output.cookie.zid; - let input = super::InputInit { - zid: output.cookie.zid, - whatami: output.cookie.whatami, - sn_resolution: output.cookie.sn_resolution, - is_shm: output.is_shm, - is_qos: output.cookie.is_qos, - }; - let transport = step!(transport_init(manager, input) - .await - .map_err(|e| (e, Some(tmsg::close_reason::INVALID)))); - - // OPEN handshake - macro_rules! step { - ($s: expr) => { - match $s { - Ok(output) => output, - Err((e, reason)) => { - match reason { - Some(tmsg::close_reason::MAX_LINKS) => log::debug!("{}", e), - _ => log::error!("{}", e), - } - if let Ok(ll) = transport.get_links() { - if ll.is_empty() { - let _ = manager.del_transport_unicast(&zid).await; - } - } - close_link(link, manager, auth_link, reason).await; - return Err(e); - } - } - }; - } - - // Add the link to the transport - step!(step!(transport - .get_inner() - .map_err(|e| (e, Some(tmsg::close_reason::INVALID)))) - .add_link(link.clone(), LinkUnicastDirection::Inbound) - .map_err(|e| (e, Some(tmsg::close_reason::MAX_LINKS)))); - - // Sync the RX sequence number - let _ = step!(transport - .get_inner() - .map_err(|e| (e, Some(tmsg::close_reason::INVALID)))) - .sync(output.initial_sn) - .await; - - log::debug!("New transport link established from {}: {}", zid, link); - - let initial_sn = step!(transport - .get_inner() - .map_err(|e| (e, Some(tmsg::close_reason::INVALID)))) - .config - .initial_sn_tx; - let input = open_ack::Input { - initial_sn, - attachment: output.open_ack_attachment, - }; - let lease = output.lease; - step!(open_ack::send(link, manager, auth_link, input).await); - - let input = InputFinalize { - transport: transport.clone(), - lease, - }; - step!(transport_finalize(link, manager, input) - .await - .map_err(|e| (e, Some(tmsg::close_reason::INVALID)))); - - Ok(()) -} diff --git a/io/zenoh-transport/src/unicast/establishment/accept/open_ack.rs b/io/zenoh-transport/src/unicast/establishment/accept/open_ack.rs deleted file mode 100644 index 5f0c887a61..0000000000 --- a/io/zenoh-transport/src/unicast/establishment/accept/open_ack.rs +++ /dev/null @@ -1,53 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use super::super::AuthenticatedPeerLink; -use super::AResult; -use crate::TransportManager; -use zenoh_link::LinkUnicast; -use zenoh_protocol::{ - common::Attachment, - core::ZInt, - transport::{tmsg, TransportMessage}, -}; - -/*************************************/ -/* ACCEPT */ -/*************************************/ -// Send an OpenAck -pub(super) struct Input { - pub(super) initial_sn: ZInt, - pub(super) attachment: Option, -} - -pub(super) async fn send( - link: &LinkUnicast, - manager: &TransportManager, - _auth_link: &AuthenticatedPeerLink, - input: Input, -) -> AResult<()> { - // Build OpenAck message - let message = TransportMessage::make_open_ack( - manager.config.unicast.lease, - input.initial_sn, - input.attachment, - ); - - // Send the message on the - let _ = link - .write_transport_message(&message) - .await - .map_err(|e| (e, Some(tmsg::close_reason::GENERIC)))?; - - Ok(()) -} diff --git a/io/zenoh-transport/src/unicast/establishment/accept/open_syn.rs b/io/zenoh-transport/src/unicast/establishment/accept/open_syn.rs deleted file mode 100644 index e226dde1b2..0000000000 --- a/io/zenoh-transport/src/unicast/establishment/accept/open_syn.rs +++ /dev/null @@ -1,175 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use super::super::{ - authenticator::AuthenticatedPeerLink, - {Cookie, EstablishmentProperties}, -}; -use super::AResult; -use crate::{unicast::establishment::cookie::Zenoh060Cookie, TransportManager}; -use std::{convert::TryFrom, time::Duration}; -use zenoh_buffers::reader::HasReader; -use zenoh_codec::{RCodec, Zenoh060}; -use zenoh_core::{zasynclock, zasyncread}; -use zenoh_crypto::hmac; -use zenoh_link::LinkUnicast; -use zenoh_protocol::{ - common::Attachment, - core::{Property, ZInt}, - transport::{tmsg, Close, TransportBody}, -}; -use zenoh_result::zerror; - -/*************************************/ -/* ACCEPT */ -/*************************************/ - -// Read and eventually accept an OpenSyn -pub(super) struct Output { - pub(super) cookie: Cookie, - pub(super) initial_sn: ZInt, - pub(super) lease: Duration, - pub(super) is_shm: bool, - pub(super) open_ack_attachment: Option, -} -#[allow(unused_mut)] -pub(super) async fn recv( - link: &LinkUnicast, - manager: &TransportManager, - auth_link: &AuthenticatedPeerLink, - input: super::init_ack::Output, -) -> AResult { - // Wait to read an OpenSyn - let mut messages = link - .read_transport_message() - .await - .map_err(|e| (e, Some(tmsg::close_reason::INVALID)))?; - if messages.len() != 1 { - let e = zerror!( - "Received multiple messages instead of a single OpenSyn on {}: {:?}", - link, - messages - ); - return Err((e.into(), Some(tmsg::close_reason::INVALID))); - } - - let mut msg = messages.remove(0); - let open_syn = match msg.body { - TransportBody::OpenSyn(open_syn) => open_syn, - TransportBody::Close(Close { reason, .. }) => { - let e = zerror!( - "Received a close message (reason {}) instead of an OpenSyn on: {:?}", - tmsg::close_reason_to_str(reason), - link, - ); - match reason { - tmsg::close_reason::MAX_LINKS => log::debug!("{}", e), - _ => log::error!("{}", e), - } - return Err((e.into(), None)); - } - _ => { - let e = zerror!( - "Received invalid message instead of an OpenSyn on {}: {:?}", - link, - msg.body - ); - log::error!("{}", e); - return Err((e.into(), Some(tmsg::close_reason::INVALID))); - } - }; - let encrypted = open_syn.cookie.to_vec(); - - // Verify that the cookie is the one we sent - if input.cookie_hash != hmac::digest(&encrypted) { - let e = zerror!("Rejecting OpenSyn on: {}. Unkwown cookie.", link); - return Err((e.into(), Some(tmsg::close_reason::INVALID))); - } - - // Decrypt the cookie with the cyper - let mut reader = encrypted.reader(); - let mut codec = Zenoh060Cookie { - prng: &mut *zasynclock!(manager.prng), - cipher: &manager.cipher, - codec: Zenoh060::default(), - }; - let mut cookie: Cookie = codec.read(&mut reader).map_err(|_| { - ( - zerror!("Decoding cookie failed").into(), - Some(tmsg::close_reason::INVALID), - ) - })?; - - // Validate with the peer authenticators - let mut open_syn_properties: EstablishmentProperties = match msg.attachment.take() { - Some(att) => EstablishmentProperties::try_from(&att) - .map_err(|e| (e, Some(tmsg::close_reason::INVALID)))?, - None => EstablishmentProperties::new(), - }; - - let mut is_shm = false; - let mut ps_attachment = EstablishmentProperties::new(); - for pa in zasyncread!(manager.state.unicast.peer_authenticator).iter() { - let po = open_syn_properties.remove(pa.id().into()).map(|x| x.value); - let pc = cookie.properties.remove(pa.id().into()).map(|x| x.value); - let mut att = pa.handle_open_syn(auth_link, &cookie, (po, pc)).await; - - #[cfg(feature = "shared-memory")] - { - if pa.id() == super::super::authenticator::PeerAuthenticatorId::Shm { - // Check if SHM has been validated from the other side - att = match att { - Ok(att) => { - is_shm = true; - Ok(att) - } - Err(e) => { - if e.is::() { - is_shm = false; - Ok(None) - } else { - Err(e) - } - } - }; - } - } - - let mut att = att.map_err(|e| (e, Some(tmsg::close_reason::INVALID)))?; - if let Some(att) = att.take() { - ps_attachment - .insert(Property { - key: pa.id().into(), - value: att, - }) - .map_err(|e| (e, Some(tmsg::close_reason::UNSUPPORTED)))?; - } - } - - let open_ack_attachment = if ps_attachment.is_empty() { - None - } else { - let att = Attachment::try_from(&ps_attachment) - .map_err(|e| (e, Some(tmsg::close_reason::INVALID)))?; - Some(att) - }; - - let output = Output { - cookie, - initial_sn: open_syn.initial_sn, - lease: open_syn.lease, - is_shm, - open_ack_attachment, - }; - Ok(output) -} diff --git a/io/zenoh-transport/src/unicast/establishment/authenticator/mod.rs b/io/zenoh-transport/src/unicast/establishment/authenticator/mod.rs deleted file mode 100644 index 48e51f7e66..0000000000 --- a/io/zenoh-transport/src/unicast/establishment/authenticator/mod.rs +++ /dev/null @@ -1,382 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -#[cfg(feature = "auth_pubkey")] -mod pubkey; -#[cfg(feature = "shared-memory")] -mod shm; -#[cfg(feature = "auth_usrpwd")] -mod userpassword; - -use crate::unicast::establishment::Cookie; -use async_trait::async_trait; -#[cfg(feature = "auth_pubkey")] -pub use pubkey::*; -#[cfg(feature = "shared-memory")] -pub use shm::*; -use std::collections::HashSet; -use std::fmt; -use std::hash::{Hash, Hasher}; -use std::ops::Deref; -use std::sync::Arc; -#[cfg(feature = "auth_usrpwd")] -pub use userpassword::*; -use zenoh_config::Config; -use zenoh_link::{Link, Locator}; -use zenoh_protocol::core::{ZInt, ZenohId}; -use zenoh_result::ZResult; - -/*************************************/ -/* LINK */ -/*************************************/ -#[derive(PartialEq, Eq, Hash)] -#[repr(u8)] -pub enum LinkAuthenticatorId { - Reserved = 0, -} - -pub struct LinkAuthenticator(Arc); - -impl LinkAuthenticator { - pub(crate) async fn from_config(_config: &Config) -> ZResult> { - Ok(HashSet::new()) - } -} - -impl Deref for LinkAuthenticator { - type Target = Arc; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl Eq for LinkAuthenticator {} - -impl PartialEq for LinkAuthenticator { - fn eq(&self, other: &Self) -> bool { - self.id() == other.id() - } -} - -impl Hash for LinkAuthenticator { - fn hash(&self, state: &mut H) { - self.id().hash(state); - } -} - -/*************************************/ -/* DUMMY LINK */ -/*************************************/ -#[async_trait] -pub trait LinkUnicastAuthenticatorTrait { - /// Return the ID of this authenticator. - fn id(&self) -> LinkAuthenticatorId; - - /// Close the authenticator - async fn close(&self); - - /// Handle new links - async fn handle_new_link(&self, link: &Link) -> ZResult>; - - /// Handle any error on a link. This callback is mainly used to clean-up any internal state - /// of the authenticator in such a way no unnecessary data is left around. - async fn handle_link_err(&self, link: &Link); -} - -pub struct DummyLinkUnicastAuthenticator; - -impl DummyLinkUnicastAuthenticator { - pub fn make() -> LinkAuthenticator { - LinkAuthenticator(Arc::new(DummyLinkUnicastAuthenticator)) - } -} - -#[async_trait] -impl LinkUnicastAuthenticatorTrait for DummyLinkUnicastAuthenticator { - fn id(&self) -> LinkAuthenticatorId { - LinkAuthenticatorId::Reserved - } - - async fn close(&self) {} - - async fn handle_new_link(&self, _link: &Link) -> ZResult> { - Ok(None) - } - - async fn handle_link_err(&self, _link: &Link) {} -} - -/*************************************/ -/* PEER */ -/*************************************/ -#[derive(PartialEq, Eq, Hash)] -#[repr(u8)] -pub enum PeerAuthenticatorId { - Reserved = 0, - Shm = 1, - UserPassword = 2, - PublicKey = 3, -} - -impl From for ZInt { - fn from(pa: PeerAuthenticatorId) -> ZInt { - pa as ZInt - } -} - -#[derive(Clone)] -pub struct PeerAuthenticator(Arc); - -impl PeerAuthenticator { - pub async fn from_config(_config: &Config) -> ZResult> { - #[allow(unused_mut)] - let mut pas = HashSet::new(); - - #[cfg(feature = "auth_pubkey")] - { - let mut res = PubKeyAuthenticator::from_config(_config).await?; - if let Some(pa) = res.take() { - pas.insert(pa.into()); - } - } - - #[cfg(feature = "auth_usrpwd")] - { - let mut res = UserPasswordAuthenticator::from_config(_config).await?; - if let Some(pa) = res.take() { - pas.insert(pa.into()); - } - } - - #[cfg(feature = "shared-memory")] - { - let mut res = SharedMemoryAuthenticator::from_config(_config).await?; - if let Some(pa) = res.take() { - pas.insert(pa.into()); - } - } - - Ok(pas) - } -} - -impl Deref for PeerAuthenticator { - type Target = Arc; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl Eq for PeerAuthenticator {} - -impl PartialEq for PeerAuthenticator { - fn eq(&self, other: &Self) -> bool { - self.id() == other.id() - } -} - -impl Hash for PeerAuthenticator { - fn hash(&self, state: &mut H) { - self.id().hash(state); - } -} - -// Authenticated peer link -#[derive(Debug)] -pub struct AuthenticatedPeerLink { - pub src: Locator, - pub dst: Locator, - pub peer_id: Option, -} - -impl fmt::Display for AuthenticatedPeerLink { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{} => {}", self.src, self.dst) - } -} - -#[async_trait] -pub trait PeerAuthenticatorTrait: Send + Sync { - /// Return the ID of this authenticator. - fn id(&self) -> PeerAuthenticatorId; - - /// Close the authenticator - async fn close(&self); - - /// Return the attachment to be included in the InitSyn message. - /// - /// # Arguments - /// * `link` - The [`AuthenticatedPeerLink`][AuthenticatedPeerLink] the initial InitSyn message will be sent on - /// - /// * `peer_id` - The [`ZenohId`][ZenohId] of the sender of the InitSyn, i.e., the peer - /// initiating a new transport. - /// - async fn get_init_syn_properties( - &self, - link: &AuthenticatedPeerLink, - peer_id: &ZenohId, - ) -> ZResult>>; - - /// Return the attachment to be included in the InitAck message to be sent - /// in response of the authenticated InitSyn. - /// - /// # Arguments - /// * `link` - The [`AuthenticatedPeerLink`][AuthenticatedPeerLink] the InitSyn message was received on - /// - /// * `cookie` - The Cookie containing the internal state - /// - /// * `property` - The optional `Property` included in the InitSyn message - /// - async fn handle_init_syn( - &self, - link: &AuthenticatedPeerLink, - cookie: &Cookie, - property: Option>, - ) -> ZResult<(Option>, Option>)>; // (Attachment, Cookie) - - /// Return the attachment to be included in the OpenSyn message to be sent - /// in response of the authenticated InitAck. - /// - /// # Arguments - /// * `link` - The [`AuthenticatedPeerLink`][AuthenticatedPeerLink] the InitSyn message was received on - /// - /// * `peer_id` - The [`ZenohId`][ZenohId] of the sender of the InitAck message - /// - /// * `sn_resolution` - The sn_resolution negotiated by the sender of the InitAck message - /// - /// * `properties` - The optional `Property` included in the InitAck message - /// - async fn handle_init_ack( - &self, - link: &AuthenticatedPeerLink, - peer_id: &ZenohId, - sn_resolution: ZInt, - property: Option>, - ) -> ZResult>>; - - /// Return the attachment to be included in the OpenAck message to be sent - /// in response of the authenticated OpenSyn. - /// - /// # Arguments - /// * `link` - The [`AuthenticatedPeerLink`][AuthenticatedPeerLink] the OpenSyn message was received on - /// - /// * `properties` - The optional `Property` included in the OpenSyn message - /// - /// * `cookie` - The optional `Property` included in the OpenSyn message - /// - async fn handle_open_syn( - &self, - link: &AuthenticatedPeerLink, - cookie: &Cookie, - property: (Option>, Option>), // (Attachment, Cookie) - ) -> ZResult>>; - - /// Auhtenticate the OpenAck. No message is sent back in response to an OpenAck - /// - /// # Arguments - /// * `link` - The [`AuthenticatedPeerLink`][AuthenticatedPeerLink] the OpenAck message was received on - /// - /// * `properties` - The optional `Property` included in the OpenAck message - /// - async fn handle_open_ack( - &self, - link: &AuthenticatedPeerLink, - property: Option>, - ) -> ZResult>>; - - /// Handle any error on a link. This callback is mainly used to clean-up any internal state - /// of the authenticator in such a way no unnecessary data is left around - /// - /// # Arguments - /// * `link` - The [`AuthenticatedPeerLink`][AuthenticatedPeerLink] generating the error - /// - async fn handle_link_err(&self, link: &AuthenticatedPeerLink); - - /// Handle any error on a link. This callback is mainly used to clean-up any internal state - /// of the authenticator in such a way no unnecessary data is left around - /// - /// # Arguments - /// * `peerd_id` - The [`ZenohId`][ZenohId] of the transport being closed. - /// - async fn handle_close(&self, peer_id: &ZenohId); -} - -/*************************************/ -/* DUMMY PEER */ -/*************************************/ -pub struct DummyPeerAuthenticator; - -impl DummyPeerAuthenticator { - pub fn make() -> PeerAuthenticator { - PeerAuthenticator(Arc::new(DummyPeerAuthenticator)) - } -} - -#[async_trait] -impl PeerAuthenticatorTrait for DummyPeerAuthenticator { - fn id(&self) -> PeerAuthenticatorId { - PeerAuthenticatorId::Reserved - } - - async fn close(&self) {} - - async fn get_init_syn_properties( - &self, - _link: &AuthenticatedPeerLink, - _peer_id: &ZenohId, - ) -> ZResult>> { - Ok(None) - } - - async fn handle_init_syn( - &self, - _link: &AuthenticatedPeerLink, - _cookie: &Cookie, - _property: Option>, - ) -> ZResult<(Option>, Option>)> { - Ok((None, None)) - } - - async fn handle_init_ack( - &self, - _link: &AuthenticatedPeerLink, - _peer_id: &ZenohId, - _sn_resolution: ZInt, - _property: Option>, - ) -> ZResult>> { - Ok(None) - } - - async fn handle_open_syn( - &self, - _link: &AuthenticatedPeerLink, - _cookie: &Cookie, - _property: (Option>, Option>), - ) -> ZResult>> { - Ok(None) - } - - async fn handle_open_ack( - &self, - _link: &AuthenticatedPeerLink, - _property: Option>, - ) -> ZResult>> { - Ok(None) - } - - async fn handle_link_err(&self, _link: &AuthenticatedPeerLink) {} - - async fn handle_close(&self, _peer_id: &ZenohId) {} -} diff --git a/io/zenoh-transport/src/unicast/establishment/authenticator/pubkey.rs b/io/zenoh-transport/src/unicast/establishment/authenticator/pubkey.rs deleted file mode 100644 index e64be6dffb..0000000000 --- a/io/zenoh-transport/src/unicast/establishment/authenticator/pubkey.rs +++ /dev/null @@ -1,687 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use super::{ - AuthenticatedPeerLink, PeerAuthenticator, PeerAuthenticatorId, PeerAuthenticatorTrait, -}; -use crate::unicast::establishment::Cookie; -use async_std::sync::Mutex; -use async_trait::async_trait; -use rand::SeedableRng; -use rsa::{ - pkcs1::{DecodeRsaPrivateKey, DecodeRsaPublicKey}, - BigUint, Pkcs1v15Encrypt, PublicKey, PublicKeyParts, RsaPrivateKey, RsaPublicKey, -}; -use std::collections::HashMap; -use std::ops::Deref; -use std::path::Path; -use std::sync::Arc; -use zenoh_buffers::{ - reader::{DidntRead, HasReader, Reader}, - writer::{DidntWrite, HasWriter, Writer}, -}; -use zenoh_cfg_properties::config::ZN_AUTH_RSA_KEY_SIZE_DEFAULT; -use zenoh_codec::{RCodec, WCodec, Zenoh060}; -use zenoh_config::Config; -use zenoh_core::{zasynclock, zparse}; -use zenoh_crypto::PseudoRng; -use zenoh_protocol::core::{ZInt, ZenohId}; -use zenoh_result::{bail, zerror, ZResult}; - -const MULTILINK_VERSION: ZInt = 1; - -/// # Attachment decorator -/// -/// ```text -/// The Attachment can decorate any message (i.e., TransportMessage and ZenohMessage) and it allows to -/// append to the message any additional information. Since the information contained in the -/// Attachement is relevant only to the layer that provided them (e.g., Transport, Zenoh, User) it -/// is the duty of that layer to serialize and de-serialize the attachment whenever deemed necessary. -/// -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+-+-+-+-+-+ -/// | ENC | ATTCH | -/// +-+-+-+---------+ -/// ~ Attachment ~ -/// +---------------+ -/// -/// ENC values: -/// - 0x00 => Zenoh Properties -/// ``` -#[repr(transparent)] -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ZPublicKey(RsaPublicKey); - -impl Deref for ZPublicKey { - type Target = RsaPublicKey; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl From for ZPublicKey { - fn from(x: RsaPublicKey) -> Self { - Self(x) - } -} - -#[repr(transparent)] -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ZPrivateKey(RsaPrivateKey); - -impl Deref for ZPrivateKey { - type Target = RsaPrivateKey; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl From for ZPrivateKey { - fn from(x: RsaPrivateKey) -> Self { - Self(x) - } -} - -impl WCodec<&ZPublicKey, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &ZPublicKey) -> Self::Output { - self.write(&mut *writer, x.n().to_bytes_le().as_slice())?; - self.write(&mut *writer, x.e().to_bytes_le().as_slice())?; - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let n: Vec = self.read(&mut *reader)?; - let n = BigUint::from_bytes_le(n.as_slice()); - let e: Vec = self.read(&mut *reader)?; - let e = BigUint::from_bytes_le(e.as_slice()); - let rsa = RsaPublicKey::new(n, e).map_err(|_| DidntRead)?; - - Ok(ZPublicKey(rsa)) - } -} - -/*************************************/ -/* InitSyn */ -/*************************************/ -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+-+-+-+-+-+ -/// |0 0 0| ATTCH | -/// +-+-+-+---------+ -/// ~ version ~ -/// +---------------+ -/// ~ public key ~ -/// +---------------+ -struct InitSynProperty { - version: ZInt, - alice_pubkey: ZPublicKey, -} - -impl WCodec<&InitSynProperty, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &InitSynProperty) -> Self::Output { - self.write(&mut *writer, x.version)?; - self.write(&mut *writer, &x.alice_pubkey)?; - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let version: ZInt = self.read(&mut *reader)?; - let alice_pubkey: ZPublicKey = self.read(&mut *reader)?; - Ok(InitSynProperty { - version, - alice_pubkey, - }) - } -} - -/*************************************/ -/* InitAck */ -/*************************************/ -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+-+-+-+-+-+ -/// |0 0 0| ATTCH | -/// +-+-+-+---------+ -/// ~ public key ~ -/// +---------------+ -/// ~ ciphered nonce~ -/// +---------------+ -struct InitAckProperty { - bob_pubkey: ZPublicKey, - nonce_encrypted_with_alice_pubkey: Vec, -} - -impl WCodec<&InitAckProperty, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &InitAckProperty) -> Self::Output { - self.write(&mut *writer, &x.bob_pubkey)?; - self.write(&mut *writer, x.nonce_encrypted_with_alice_pubkey.as_slice())?; - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let bob_pubkey: ZPublicKey = self.read(&mut *reader)?; - let nonce_encrypted_with_alice_pubkey: Vec = self.read(&mut *reader)?; - Ok(InitAckProperty { - bob_pubkey, - nonce_encrypted_with_alice_pubkey, - }) - } -} - -/*************************************/ -/* OpenSyn */ -/*************************************/ -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+-+-+-+-+-+ -/// |0 0 0| ATTCH | -/// +-+-+-+---------+ -/// ~ ciphered nonce~ -/// +---------------+ -struct OpenSynProperty { - nonce_encrypted_with_bob_pubkey: Vec, -} - -impl WCodec<&OpenSynProperty, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &OpenSynProperty) -> Self::Output { - self.write(&mut *writer, x.nonce_encrypted_with_bob_pubkey.as_slice())?; - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let nonce_encrypted_with_bob_pubkey: Vec = self.read(&mut *reader)?; - Ok(OpenSynProperty { - nonce_encrypted_with_bob_pubkey, - }) - } -} - -/*************************************/ -/* Authenticator */ -/*************************************/ -struct InnerState { - prng: PseudoRng, - known_keys: Option>, - authenticated: HashMap>, -} - -pub struct PubKeyAuthenticator { - pub_key: ZPublicKey, - pri_key: ZPrivateKey, - state: Mutex, -} - -impl PubKeyAuthenticator { - pub fn new(pub_key: T, pri_key: U) -> PubKeyAuthenticator - where - T: Into, - U: Into, - { - PubKeyAuthenticator { - pub_key: pub_key.into(), - pri_key: pri_key.into(), - state: Mutex::new(InnerState { - prng: PseudoRng::from_entropy(), - known_keys: None, - authenticated: HashMap::new(), - }), - } - } - - pub fn make() -> ZResult { - let mut prng = PseudoRng::from_entropy(); - let bits = zparse!(ZN_AUTH_RSA_KEY_SIZE_DEFAULT)?; - let pri_key = RsaPrivateKey::new(&mut prng, bits)?; - let pub_key = RsaPublicKey::from(&pri_key); - - let pka = PubKeyAuthenticator { - pub_key: pub_key.into(), - pri_key: pri_key.into(), - state: Mutex::new(InnerState { - prng, - known_keys: None, - authenticated: HashMap::new(), - }), - }; - Ok(pka) - } - - pub async fn add_key(&self, key: ZPublicKey) -> ZResult<()> { - let mut guard = zasynclock!(self.state); - match guard.known_keys.as_mut() { - Some(kk) => { - if !kk.iter().any(|x| x == &key) { - kk.push(key); - } - } - None => { - let hs = vec![key]; - guard.known_keys = Some(hs); - } - } - Ok(()) - } - - pub async fn del_key(&self, key: &ZPublicKey) -> ZResult<()> { - let mut guard = zasynclock!(self.state); - if let Some(kk) = guard.known_keys.as_mut() { - if let Some(i) = kk.iter().position(|x| x == key) { - kk.remove(i); - } - } - Ok(()) - } - - pub async fn from_config(config: &Config) -> ZResult> { - let c = config.transport().auth().pubkey(); - - // @TODO: support PubKey keys import - - // First, check if PEM keys are provided - match (c.public_key_pem(), c.private_key_pem()) { - (Some(public), Some(private)) => { - let pub_key = RsaPublicKey::from_pkcs1_pem(public) - .map_err(|e| zerror!("Rsa Public Key: {}", e))?; - let pri_key = RsaPrivateKey::from_pkcs1_pem(private) - .map_err(|e| zerror!("Rsa Private Key: {}", e))?; - return Ok(Some(Self::new(pub_key, pri_key))); - } - (Some(_), None) => { - bail!("Missing Rsa Private Key: PEM") - } - (None, Some(_)) => { - bail!("Missing Rsa Public Key: PEM") - } - (None, None) => {} - } - - // Second, check if PEM files are provided - match (c.public_key_file(), c.private_key_file()) { - (Some(public), Some(private)) => { - let path = Path::new(public); - let pub_key = RsaPublicKey::read_pkcs1_pem_file(path) - .map_err(|e| zerror!("Rsa Public Key: {}", e))?; - let path = Path::new(private); - let pri_key = RsaPrivateKey::read_pkcs1_pem_file(path) - .map_err(|e| zerror!("Rsa Private Key: {}", e))?; - return Ok(Some(Self::new(pub_key, pri_key))); - } - (Some(_), None) => { - bail!("Missing Rsa Private Key: file") - } - (None, Some(_)) => { - bail!("Missing Rsa Public Key: file") - } - (None, None) => {} - } - - Ok(None) - } -} - -#[async_trait] -impl PeerAuthenticatorTrait for PubKeyAuthenticator { - fn id(&self) -> PeerAuthenticatorId { - PeerAuthenticatorId::PublicKey - } - - async fn close(&self) { - // No cleanup needed - } - - async fn get_init_syn_properties( - &self, - link: &AuthenticatedPeerLink, - _peer_id: &ZenohId, - ) -> ZResult>> { - let init_syn_property = InitSynProperty { - version: MULTILINK_VERSION, - alice_pubkey: self.pub_key.clone(), - }; - - let mut wbuf = vec![]; - let codec = Zenoh060::default(); - let mut writer = wbuf.writer(); - codec - .write(&mut writer, &init_syn_property) - .map_err(|_| zerror!("Error in encoding InitSyn for PubKey on link: {}", link))?; - - Ok(Some(wbuf)) - } - - async fn handle_init_syn( - &self, - link: &AuthenticatedPeerLink, - cookie: &Cookie, - property: Option>, - ) -> ZResult<(Option>, Option>)> { - match property { - // The connecting zenoh peer wants to do multilink - Some(pk) => { - // Decode the multilink attachment - let mut reader = pk.reader(); - let codec = Zenoh060::default(); - - let init_syn_property: InitSynProperty = codec.read(&mut reader).map_err(|_| { - zerror!( - "Received InitSyn with invalid PubKey attachment on link: {}", - link - ) - })?; - - // Check if we are compatible - if init_syn_property.version != MULTILINK_VERSION { - bail!("PubKey version not supported on link: {}", link); - } - - // Check if the peer is already present - let mut guard = zasynclock!(self.state); - match guard.authenticated.get(&cookie.zid) { - Some(alice_pubkey) => { - // Check if the public key is the same - match alice_pubkey.as_ref() { - Some(apk) => { - // Check if pub key is used consistently - if apk != &init_syn_property.alice_pubkey { - bail!("Invalid multilink PubKey on link: {}", link); - } - } - None => { - // The peer is already present but no previous multilink intereset - // was declared. Rejecting for inconsistent declaration. - bail!("Unexpected multilink PubKey on link: {}", link); - } - } - } - None => { - // It's the first time we see this peer, check if it is authorized it - if let Some(kk) = guard.known_keys.as_ref() { - if !kk.iter().any(|x| x == &init_syn_property.alice_pubkey) { - // The peer is already present but no previous multilink intereset - // was declared. Rejecting for inconsistent declaration. - bail!("Unauthorized multilink PubKey on link: {}", link); - } - } - - guard - .authenticated - .insert(cookie.zid, Some(init_syn_property.alice_pubkey.clone())); - } - } - - // Create the InitAck attachment - let codec = Zenoh060::default(); - - let mut wbuf = vec![]; - let mut writer = wbuf.writer(); - codec.write(&mut writer, cookie.nonce).map_err(|_| { - zerror!("Error in encoding InitAck for PubKey on link: {}", link) - })?; - - let nonce_bytes = wbuf; - let nonce_encrypted_with_alice_pubkey = init_syn_property.alice_pubkey.encrypt( - &mut guard.prng, - Pkcs1v15Encrypt, - nonce_bytes.as_slice(), - )?; - - let init_ack_property = InitAckProperty { - bob_pubkey: self.pub_key.clone(), - nonce_encrypted_with_alice_pubkey, - }; - - // Store the public key in the cookie - let mut wbuf = vec![]; - let mut writer = wbuf.writer(); - codec - .write(&mut writer, &init_syn_property.alice_pubkey) - .map_err(|_| { - zerror!("Error in encoding InitAck for PubKey on link: {}", link) - })?; - let cookie = wbuf; - - // Encode the InitAck property - let mut wbuf = vec![]; - let mut writer = wbuf.writer(); - codec.write(&mut writer, &init_ack_property).map_err(|_| { - zerror!("Error in encoding InitAck for PubKey on link: {}", link) - })?; - let attachment = wbuf; - - Ok((Some(attachment), Some(cookie))) - } - // The connecting zenoh peer does not want to do multilink - None => { - let guard = zasynclock!(self.state); - if guard.authenticated.get(&cookie.zid).is_some() { - // The peer is already present but no multilink intereset is declared. - // Rejecting for inconsistent declaration. - bail!("No multilink supported on link: {}", link); - } - - // No properties need to be included in the InitAck attachment - Ok((None, None)) - } - } - } - - async fn handle_init_ack( - &self, - link: &AuthenticatedPeerLink, - _peer_id: &ZenohId, - _sn_resolution: ZInt, - property: Option>, - ) -> ZResult>> { - let pk = match property { - Some(pk) => pk, - None => return Ok(None), - }; - - let codec = Zenoh060::default(); - - let mut reader = pk.reader(); - let init_ack_property: InitAckProperty = codec.read(&mut reader).map_err(|_| { - zerror!( - "Received InitAck with invalid PubKey attachment on link: {}", - link - ) - })?; - let nonce = self.pri_key.decrypt( - Pkcs1v15Encrypt, - init_ack_property - .nonce_encrypted_with_alice_pubkey - .as_slice(), - )?; - - // Create the OpenSyn attachment - let mut guard = zasynclock!(self.state); - let nonce_encrypted_with_bob_pubkey = - init_ack_property - .bob_pubkey - .encrypt(&mut guard.prng, Pkcs1v15Encrypt, &nonce[..])?; - drop(guard); - - let open_syn_property = OpenSynProperty { - nonce_encrypted_with_bob_pubkey, - }; - - // Encode the OpenSyn property - let mut wbuf = vec![]; - let mut writer = wbuf.writer(); - codec - .write(&mut writer, &open_syn_property) - .map_err(|_| zerror!("Error in encoding OpenSyn for PubKey on link: {}", link))?; - let attachment = wbuf; - - Ok(Some(attachment)) - } - - async fn handle_open_syn( - &self, - link: &AuthenticatedPeerLink, - cookie: &Cookie, - property: (Option>, Option>), - ) -> ZResult>> { - match property { - (Some(att), Some(cke)) => { - let codec = Zenoh060::default(); - - let mut reader = att.reader(); - let open_syn_property: OpenSynProperty = codec.read(&mut reader).map_err(|_| { - zerror!( - "Received OpenSyn with invalid PubKey attachment on link: {}", - link - ) - })?; - - let nonce_bytes = self.pri_key.decrypt( - Pkcs1v15Encrypt, - open_syn_property.nonce_encrypted_with_bob_pubkey.as_slice(), - )?; - let mut reader = nonce_bytes.reader(); - let nonce: ZInt = codec.read(&mut reader).map_err(|_| { - zerror!( - "Received OpenSyn with invalid PubKey attachment on link: {}", - link - ) - })?; - - if nonce != cookie.nonce { - bail!("Received invalid nonce on link: {}", link); - } - - let mut reader = cke.reader(); - let alice_pubkey: ZPublicKey = codec.read(&mut reader).map_err(|_| { - zerror!( - "Received OpenSyn with invalid PubKey attachment on link: {}", - link - ) - })?; - - let mut guard = zasynclock!(self.state); - match guard.authenticated.get(&cookie.zid) { - Some(apk) => match apk { - Some(apk) => { - // Check if the public key is still correct - if apk != &alice_pubkey { - bail!("Invalid multilink pub key on link: {}", link); - } - } - None => { - // The peer did not previously express interest in multilink - bail!("Invalid multilink pub key on link: {}", link); - } - }, - None => { - // Finally store the public key - guard.authenticated.insert(cookie.zid, Some(alice_pubkey)); - } - } - } - (None, None) => { - // No multilink - let mut guard = zasynclock!(self.state); - if guard.authenticated.get(&cookie.zid).is_some() { - // The peer did not previously express interest in multilink - bail!("Invalid multilink pub key on link: {}", link); - } - // Finally store the public key - guard.authenticated.insert(cookie.zid, None); - } - _ => { - bail!("Received invalid nonce on link: {}", link); - } - } - - Ok(None) - } - - async fn handle_open_ack( - &self, - _link: &AuthenticatedPeerLink, - _property: Option>, - ) -> ZResult>> { - Ok(None) - } - - async fn handle_link_err(&self, link: &AuthenticatedPeerLink) { - // Need to check if it authenticated and remove it if this is the last link - if let Some(zid) = link.peer_id.as_ref() { - zasynclock!(self.state).authenticated.remove(zid); - } - } - - async fn handle_close(&self, peer_id: &ZenohId) { - zasynclock!(self.state).authenticated.remove(peer_id); - } -} - -//noinspection ALL -impl From> for PeerAuthenticator { - fn from(v: Arc) -> PeerAuthenticator { - PeerAuthenticator(v) - } -} - -impl From for PeerAuthenticator { - fn from(v: PubKeyAuthenticator) -> PeerAuthenticator { - Self::from(Arc::new(v)) - } -} diff --git a/io/zenoh-transport/src/unicast/establishment/authenticator/shm.rs b/io/zenoh-transport/src/unicast/establishment/authenticator/shm.rs deleted file mode 100644 index 8b3a2d37a1..0000000000 --- a/io/zenoh-transport/src/unicast/establishment/authenticator/shm.rs +++ /dev/null @@ -1,446 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use super::{ - AuthenticatedPeerLink, PeerAuthenticator, PeerAuthenticatorId, PeerAuthenticatorTrait, -}; -use crate::unicast::establishment::Cookie; -use async_trait::async_trait; -use rand::{Rng, SeedableRng}; -use std::{ - convert::TryInto, - sync::{Arc, RwLock}, -}; -use zenoh_buffers::{ - reader::{DidntRead, HasReader, Reader}, - writer::{DidntWrite, HasWriter, Writer}, - ZSlice, -}; -use zenoh_codec::{RCodec, WCodec, Zenoh060}; -use zenoh_config::Config; -use zenoh_crypto::PseudoRng; -use zenoh_protocol::core::{ZInt, ZenohId}; -use zenoh_result::{bail, zerror, ShmError, ZResult}; -use zenoh_shm::{ - SharedMemoryBuf, SharedMemoryBufInfoSerialized, SharedMemoryManager, SharedMemoryReader, -}; - -const SHM_VERSION: ZInt = 0; -const SHM_NAME: &str = "shmauth"; -// Let's use a ZInt as a challenge -const SHM_SIZE: usize = std::mem::size_of::(); - -/*************************************/ -/* InitSyn */ -/*************************************/ -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+-+-+-+-+-+ -/// |0 0 0| ATTCH | -/// +-+-+-+---------+ -/// ~ version ~ -/// +---------------+ -/// ~ ShmMemBuf ~ -/// +---------------+ -struct InitSynProperty { - version: ZInt, - shm: ZSlice, -} - -impl WCodec<&InitSynProperty, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &InitSynProperty) -> Self::Output { - self.write(&mut *writer, x.version)?; - self.write(&mut *writer, &x.shm)?; - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let version: ZInt = self.read(&mut *reader)?; - let bytes: Vec = self.read(&mut *reader)?; - let shm_info: SharedMemoryBufInfoSerialized = bytes.into(); - let shm: ZSlice = shm_info.into(); - Ok(InitSynProperty { version, shm }) - } -} - -/*************************************/ -/* InitAck */ -/*************************************/ -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+-+-+-+-+-+ -/// |0 0 0| ATTCH | -/// +-+-+-+---------+ -/// ~ challenge ~ -/// +---------------+ -/// ~ ShmMemBuf ~ -/// +---------------+ -struct InitAckProperty { - challenge: ZInt, - shm: ZSlice, -} - -impl WCodec<&InitAckProperty, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &InitAckProperty) -> Self::Output { - self.write(&mut *writer, x.challenge)?; - self.write(&mut *writer, &x.shm)?; - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let challenge: ZInt = self.read(&mut *reader)?; - let bytes: Vec = self.read(&mut *reader)?; - let shm_info: SharedMemoryBufInfoSerialized = bytes.into(); - let shm: ZSlice = shm_info.into(); - Ok(InitAckProperty { challenge, shm }) - } -} - -/*************************************/ -/* OpenSyn */ -/*************************************/ -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+-+-+-+-+-+ -/// |0 0 0| ATTCH | -/// +-+-+-+---------+ -/// ~ challenge ~ -/// +---------------+ -struct OpenSynProperty { - challenge: ZInt, -} - -impl WCodec<&OpenSynProperty, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &OpenSynProperty) -> Self::Output { - self.write(&mut *writer, x.challenge)?; - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let challenge: ZInt = self.read(&mut *reader)?; - Ok(OpenSynProperty { challenge }) - } -} - -/*************************************/ -/* Authenticator */ -/*************************************/ -pub struct SharedMemoryAuthenticator { - challenge: ZInt, - // Rust guarantees that fields are dropped in the order of declaration. - // Buffer needs to be dropped before the manager. - buffer: SharedMemoryBuf, - _manager: SharedMemoryManager, - reader: Arc>, -} - -impl SharedMemoryAuthenticator { - pub fn make() -> ZResult { - let mut prng = PseudoRng::from_entropy(); - let challenge = prng.gen::(); - - let mut _manager = SharedMemoryManager::make(format!("{SHM_NAME}.{challenge}"), SHM_SIZE)?; - - let mut buffer = _manager.alloc(SHM_SIZE).unwrap(); - let slice = unsafe { buffer.as_mut_slice() }; - slice[0..SHM_SIZE].copy_from_slice(&challenge.to_le_bytes()); - - let shmauth = SharedMemoryAuthenticator { - challenge, - buffer, - _manager, - reader: Arc::new(RwLock::new(SharedMemoryReader::new())), - }; - Ok(shmauth) - } - - pub async fn from_config(config: &Config) -> ZResult> { - if *config.transport().shared_memory().enabled() { - let mut prng = PseudoRng::from_entropy(); - let challenge = prng.gen::(); - - let mut _manager = - SharedMemoryManager::make(format!("{SHM_NAME}.{challenge}"), SHM_SIZE)?; - - let mut buffer = _manager.alloc(SHM_SIZE)?; - let slice = unsafe { buffer.as_mut_slice() }; - slice[0..SHM_SIZE].copy_from_slice(&challenge.to_le_bytes()); - - let sma = SharedMemoryAuthenticator { - challenge, - buffer, - _manager, - reader: Arc::new(RwLock::new(SharedMemoryReader::new())), - }; - Ok(Some(sma)) - } else { - Ok(None) - } - } -} - -unsafe impl Send for SharedMemoryAuthenticator {} -unsafe impl Sync for SharedMemoryAuthenticator {} - -#[async_trait] -impl PeerAuthenticatorTrait for SharedMemoryAuthenticator { - fn id(&self) -> PeerAuthenticatorId { - PeerAuthenticatorId::Shm - } - - async fn close(&self) { - // No cleanup needed - } - - async fn get_init_syn_properties( - &self, - link: &AuthenticatedPeerLink, - _peer_id: &ZenohId, - ) -> ZResult>> { - let init_syn_property = InitSynProperty { - version: SHM_VERSION, - shm: self.buffer.info.serialize().unwrap().into(), - }; - let mut buff = vec![]; - let codec = Zenoh060::default(); - - let mut writer = buff.writer(); - codec - .write(&mut writer, &init_syn_property) - .map_err(|_| zerror!("Error in encoding InitSyn for SHM on link: {}", link))?; - - Ok(Some(buff)) - } - - async fn handle_init_syn( - &self, - link: &AuthenticatedPeerLink, - cookie: &Cookie, - mut property: Option>, - ) -> ZResult<(Option>, Option>)> { - let buffer = match property.take() { - Some(p) => p, - None => { - log::debug!("Peer {} did not express interest in SHM", cookie.zid); - return Ok((None, None)); - } - }; - - let codec = Zenoh060::default(); - let mut reader = buffer.reader(); - - let mut init_syn_property: InitSynProperty = codec - .read(&mut reader) - .map_err(|_| zerror!("Received InitSyn with invalid attachment on link: {}", link))?; - - if init_syn_property.version > SHM_VERSION { - bail!("Rejected InitSyn with invalid attachment on link: {}", link) - } - - // Try to read from the shared memory - match crate::shm::map_zslice_to_shmbuf(&mut init_syn_property.shm, &self.reader) { - Ok(res) => { - if !res { - log::debug!("Peer {} can not operate over SHM: error", cookie.zid); - return Ok((None, None)); - } - } - Err(e) => { - log::debug!("Peer {} can not operate over SHM: {}", cookie.zid, e); - return Ok((None, None)); - } - } - - log::debug!("Authenticating Shared Memory Access..."); - - let xs = init_syn_property.shm; - let bytes: [u8; SHM_SIZE] = match xs.as_slice().try_into() { - Ok(bytes) => bytes, - Err(e) => { - log::debug!("Peer {} can not operate over SHM: {}", cookie.zid, e); - return Ok((None, None)); - } - }; - let challenge = ZInt::from_le_bytes(bytes); - - // Create the InitAck attachment - let init_ack_property = InitAckProperty { - challenge, - shm: self.buffer.info.serialize().unwrap().into(), - }; - // Encode the InitAck property - let mut buffer = vec![]; - let mut writer = buffer.writer(); - codec - .write(&mut writer, &init_ack_property) - .map_err(|_| zerror!("Error in encoding InitSyn for SHM on link: {}", link))?; - - Ok((Some(buffer), None)) - } - - async fn handle_init_ack( - &self, - link: &AuthenticatedPeerLink, - peer_id: &ZenohId, - _sn_resolution: ZInt, - mut property: Option>, - ) -> ZResult>> { - let buffer = match property.take() { - Some(p) => p, - None => { - log::debug!("Peer {} did not express interest in SHM", peer_id); - return Ok(None); - } - }; - - let codec = Zenoh060::default(); - let mut reader = buffer.reader(); - - let mut init_ack_property: InitAckProperty = codec - .read(&mut reader) - .map_err(|_| zerror!("Received InitAck with invalid attachment on link: {}", link))?; - - // Try to read from the shared memory - match crate::shm::map_zslice_to_shmbuf(&mut init_ack_property.shm, &self.reader) { - Ok(res) => { - if !res { - return Err(ShmError(zerror!("No SHM on link: {}", link)).into()); - } - } - Err(e) => return Err(ShmError(zerror!("No SHM on link {}: {}", link, e)).into()), - } - - let bytes: [u8; SHM_SIZE] = init_ack_property.shm.as_slice().try_into().map_err(|e| { - zerror!( - "Received InitAck with invalid attachment on link {}: {}", - link, - e - ) - })?; - let challenge = ZInt::from_le_bytes(bytes); - - if init_ack_property.challenge == self.challenge { - // Create the OpenSyn attachment - let open_syn_property = OpenSynProperty { challenge }; - // Encode the OpenSyn property - let mut buffer = vec![]; - let mut writer = buffer.writer(); - - codec - .write(&mut writer, &open_syn_property) - .map_err(|_| zerror!("Error in encoding OpenSyn for SHM on link: {}", link))?; - - Ok(Some(buffer)) - } else { - Err(ShmError(zerror!( - "Received OpenSyn with invalid attachment on link: {}", - link - )) - .into()) - } - } - - async fn handle_open_syn( - &self, - link: &AuthenticatedPeerLink, - _cookie: &Cookie, - property: (Option>, Option>), - ) -> ZResult>> { - let (mut attachment, _cookie) = property; - let buffer = match attachment.take() { - Some(p) => p, - None => { - return Err(ShmError(zerror!( - "Received OpenSyn with no SHM attachment on link: {}", - link - )) - .into()); - } - }; - - let codec = Zenoh060::default(); - let mut reader = buffer.reader(); - - let open_syn_property: OpenSynProperty = codec - .read(&mut reader) - .map_err(|_| zerror!("Received OpenSyn with invalid attachment on link: {}", link))?; - - if open_syn_property.challenge == self.challenge { - Ok(None) - } else { - Err(ShmError(zerror!( - "Received OpenSyn with invalid attachment on link: {}", - link - )) - .into()) - } - } - - async fn handle_open_ack( - &self, - _link: &AuthenticatedPeerLink, - _property: Option>, - ) -> ZResult>> { - Ok(None) - } - - async fn handle_link_err(&self, _link: &AuthenticatedPeerLink) {} - - async fn handle_close(&self, _peer_id: &ZenohId) {} -} - -//noinspection ALL -impl From> for PeerAuthenticator { - fn from(v: Arc) -> PeerAuthenticator { - PeerAuthenticator(v) - } -} - -impl From for PeerAuthenticator { - fn from(v: SharedMemoryAuthenticator) -> PeerAuthenticator { - Self::from(Arc::new(v)) - } -} diff --git a/io/zenoh-transport/src/unicast/establishment/authenticator/userpassword.rs b/io/zenoh-transport/src/unicast/establishment/authenticator/userpassword.rs deleted file mode 100644 index 6e77128ea9..0000000000 --- a/io/zenoh-transport/src/unicast/establishment/authenticator/userpassword.rs +++ /dev/null @@ -1,476 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use super::{ - AuthenticatedPeerLink, PeerAuthenticator, PeerAuthenticatorId, PeerAuthenticatorTrait, -}; -use super::{Locator, ZInt, ZenohId}; -use crate::unicast::establishment::Cookie; -use async_std::fs; -use async_std::sync::{Mutex, RwLock}; -use async_trait::async_trait; -use std::collections::{HashMap, HashSet}; -use std::sync::Arc; -use zenoh_buffers::{ - reader::{DidntRead, HasReader, Reader}, - writer::{DidntWrite, HasWriter, Writer}, -}; -use zenoh_cfg_properties::Properties; -use zenoh_codec::{RCodec, WCodec, Zenoh060}; -use zenoh_config::Config; -use zenoh_core::{zasynclock, zasyncread, zasyncwrite}; -use zenoh_crypto::hmac; -use zenoh_result::{bail, zerror, ZResult}; - -const USRPWD_VERSION: ZInt = 1; - -/// # Attachment decorator -/// -/// ```text -/// The Attachment can decorate any message (i.e., TransportMessage and ZenohMessage) and it allows to -/// append to the message any additional information. Since the information contained in the -/// Attachement is relevant only to the layer that provided them (e.g., Transport, Zenoh, User) it -/// is the duty of that layer to serialize and de-serialize the attachment whenever deemed necessary. -/// -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+-+-+-+-+-+ -/// | ENC | ATTCH | -/// +-+-+-+---------+ -/// ~ Attachment ~ -/// +---------------+ -/// -/// ENC values: -/// - 0x00 => Zenoh Properties -/// ``` - -/*************************************/ -/* InitSyn */ -/*************************************/ -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+-+-+-+-+-+ -/// |0 0 0| ATTCH | -/// +-+-+-+---------+ -/// ~ version ~ -/// +---------------+ -struct InitSynProperty { - version: ZInt, -} - -impl WCodec<&InitSynProperty, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &InitSynProperty) -> Self::Output { - self.write(&mut *writer, x.version)?; - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let version: ZInt = self.read(&mut *reader)?; - Ok(InitSynProperty { version }) - } -} - -/*************************************/ -/* InitAck */ -/*************************************/ -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+-+-+-+-+-+ -/// |0 0 0| ATTCH | -/// +-+-+-+---------+ -/// ~ nonce ~ -/// +---------------+ -struct InitAckProperty { - nonce: ZInt, -} - -impl WCodec<&InitAckProperty, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &InitAckProperty) -> Self::Output { - self.write(&mut *writer, x.nonce)?; - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let nonce: ZInt = self.read(&mut *reader)?; - Ok(InitAckProperty { nonce }) - } -} - -/*************************************/ -/* OpenSyn */ -/*************************************/ -/// 7 6 5 4 3 2 1 0 -/// +-+-+-+-+-+-+-+-+ -/// |0 0 0| ATTCH | -/// +-+-+-+---------+ -/// ~ user ~ -/// +---------------+ -/// ~ hash ~ -/// +---------------+ -struct OpenSynProperty { - user: Vec, - hmac: Vec, -} - -impl WCodec<&OpenSynProperty, &mut W> for Zenoh060 -where - W: Writer, -{ - type Output = Result<(), DidntWrite>; - - fn write(self, writer: &mut W, x: &OpenSynProperty) -> Self::Output { - self.write(&mut *writer, x.user.as_slice())?; - self.write(&mut *writer, x.hmac.as_slice())?; - Ok(()) - } -} - -impl RCodec for Zenoh060 -where - R: Reader, -{ - type Error = DidntRead; - - fn read(self, reader: &mut R) -> Result { - let user: Vec = self.read(&mut *reader)?; - let hmac: Vec = self.read(&mut *reader)?; - Ok(OpenSynProperty { user, hmac }) - } -} - -/*************************************/ -/* Authenticator */ -/*************************************/ -struct Credentials { - user: Vec, - password: Vec, -} - -struct Authenticated { - credentials: Credentials, - links: HashSet<(Locator, Locator)>, -} - -pub struct UserPasswordAuthenticator { - lookup: RwLock, Vec>>, - credentials: Option, - authenticated: Mutex>, -} - -impl UserPasswordAuthenticator { - pub fn new( - lookup: HashMap, Vec>, - mut credentials: Option<(Vec, Vec)>, - ) -> UserPasswordAuthenticator { - let credentials = credentials.take().map(|cr| Credentials { - user: cr.0, - password: cr.1, - }); - UserPasswordAuthenticator { - lookup: RwLock::new(lookup), - credentials, - authenticated: Mutex::new(HashMap::new()), - } - } - - pub async fn add_user(&self, user: Vec, password: Vec) -> ZResult<()> { - let mut guard = zasyncwrite!(self.lookup); - guard.insert(user, password); - Ok(()) - } - - pub async fn del_user(&self, user: &[u8]) -> ZResult<()> { - let mut guard = zasyncwrite!(self.lookup); - guard.remove(user); - Ok(()) - } - - pub async fn from_config(config: &Config) -> ZResult> { - let c = config.transport().auth().usrpwd(); - - let mut lookup: HashMap, Vec> = HashMap::new(); - if let Some(dict) = c.dictionary_file() { - let content = fs::read_to_string(dict) - .await - .map_err(|e| zerror!("Invalid user-password dictionary file: {}", e))?; - // Populate the user-password dictionary - let mut ps = Properties::from(content); - for (user, password) in ps.drain() { - lookup.insert(user.into(), password.into()); - } - log::debug!("User-password dictionary has been configured"); - } - - let mut credentials: Option<(Vec, Vec)> = None; - if let Some(user) = c.user() { - if let Some(password) = c.password() { - log::debug!("User and password have been configured"); - credentials = Some((user.to_string().into(), password.to_string().into())); - } - } - - if !lookup.is_empty() || credentials.is_some() { - log::debug!("User-password authentication is enabled"); - Ok(Some(UserPasswordAuthenticator::new(lookup, credentials))) - } else { - Ok(None) - } - } -} - -#[async_trait] -impl PeerAuthenticatorTrait for UserPasswordAuthenticator { - fn id(&self) -> PeerAuthenticatorId { - PeerAuthenticatorId::UserPassword - } - - async fn close(&self) { - // No cleanup needed - } - - async fn get_init_syn_properties( - &self, - link: &AuthenticatedPeerLink, - _peer_id: &ZenohId, - ) -> ZResult>> { - // If credentials are not configured, don't initiate the USRPWD authentication - if self.credentials.is_none() { - return Ok(None); - } - - let init_syn_property = InitSynProperty { - version: USRPWD_VERSION, - }; - let mut wbuf = vec![]; - let codec = Zenoh060::default(); - let mut writer = wbuf.writer(); - codec - .write(&mut writer, &init_syn_property) - .map_err(|_| zerror!("Error in encoding InitSyn for UsrPwd on link: {}", link))?; - let attachment = wbuf; - - Ok(Some(attachment)) - } - - async fn handle_init_syn( - &self, - link: &AuthenticatedPeerLink, - cookie: &Cookie, - property: Option>, - ) -> ZResult<(Option>, Option>)> { - let p = property.ok_or_else(|| { - zerror!( - "Received InitSyn with no UsrPwd attachment on link: {}", - link - ) - })?; - - let codec = Zenoh060::default(); - - let mut reader = p.reader(); - let init_syn_property: InitSynProperty = codec.read(&mut reader).map_err(|_| { - zerror!( - "Received InitSyn with invalid UsrPwd attachment on link: {}", - link - ) - })?; - if init_syn_property.version > USRPWD_VERSION { - bail!("Rejected InitSyn with invalid attachment on link: {}", link) - } - - // Create the InitAck attachment - let init_ack_property = InitAckProperty { - nonce: cookie.nonce, - }; - let mut wbuf = vec![]; - let mut writer = wbuf.writer(); - codec - .write(&mut writer, &init_ack_property) - .map_err(|_| zerror!("Error in encoding InitAck for UsrPwd on link: {}", link))?; - let attachment = wbuf; - - Ok((Some(attachment), None)) - } - - async fn handle_init_ack( - &self, - link: &AuthenticatedPeerLink, - _peer_id: &ZenohId, - _sn_resolution: ZInt, - property: Option>, - ) -> ZResult>> { - // If credentials are not configured, don't continue the USRPWD authentication - let credentials = match self.credentials.as_ref() { - Some(cr) => cr, - None => return Ok(None), - }; - - let p = property.ok_or_else(|| { - zerror!( - "Received InitAck with no UsrPwd attachment on link: {}", - link - ) - })?; - - let codec = Zenoh060::default(); - - let mut reader = p.reader(); - let init_ack_property: InitAckProperty = codec.read(&mut reader).map_err(|_| { - zerror!( - "Received InitAck with invalid UsrPwd attachment on link: {}", - link - ) - })?; - - // Create the HMAC of the password using the nonce received as a key (it's a challenge) - let key = init_ack_property.nonce.to_le_bytes(); - let hmac = hmac::sign(&key, &credentials.password)?; - // Create the OpenSyn attachment - let open_syn_property = OpenSynProperty { - user: credentials.user.clone(), - hmac, - }; - // Encode the InitAck attachment - let mut wbuf = vec![]; - let mut writer = wbuf.writer(); - codec - .write(&mut writer, &open_syn_property) - .map_err(|_| zerror!("Error in encoding OpenSyn for UsrPwd on link: {}", link))?; - let attachment = wbuf; - - Ok(Some(attachment)) - } - - async fn handle_open_syn( - &self, - link: &AuthenticatedPeerLink, - cookie: &Cookie, - property: (Option>, Option>), - ) -> ZResult>> { - let (attachment, _cookie) = property; - let a = attachment.ok_or_else(|| { - zerror!( - "Received OpenSyn with no UsrPwd attachment on link: {}", - link - ) - })?; - - let codec = Zenoh060::default(); - - let mut reader = a.reader(); - let open_syn_property: OpenSynProperty = codec.read(&mut reader).map_err(|_| { - zerror!( - "Received OpenSyn with invalid UsrPwd attachment on link: {}", - link - ) - })?; - let password = match zasyncread!(self.lookup).get(&open_syn_property.user) { - Some(password) => password.clone(), - None => bail!("Received OpenSyn with invalid user on link: {}", link), - }; - - // Create the HMAC of the password using the nonce received as challenge - let key = cookie.nonce.to_le_bytes(); - let hmac = hmac::sign(&key, &password)?; - if hmac != open_syn_property.hmac { - bail!("Received OpenSyn with invalid password on link: {}", link) - } - - // Check PID validity - let mut guard = zasynclock!(self.authenticated); - match guard.get_mut(&cookie.zid) { - Some(auth) => { - if open_syn_property.user != auth.credentials.user - || password != auth.credentials.password - { - bail!("Received OpenSyn with invalid password on link: {}", link) - } - auth.links.insert((link.src.clone(), link.dst.clone())); - } - None => { - let credentials = Credentials { - user: open_syn_property.user, - password, - }; - let mut links = HashSet::new(); - links.insert((link.src.clone(), link.dst.clone())); - let auth = Authenticated { credentials, links }; - guard.insert(cookie.zid, auth); - } - } - - Ok(None) - } - - async fn handle_open_ack( - &self, - _link: &AuthenticatedPeerLink, - _property: Option>, - ) -> ZResult>> { - Ok(None) - } - - async fn handle_link_err(&self, link: &AuthenticatedPeerLink) { - // Need to check if it authenticated and remove it if this is the last link - let mut guard = zasynclock!(self.authenticated); - let mut to_del: Option = None; - for (peer_id, auth) in guard.iter_mut() { - auth.links.remove(&(link.src.clone(), link.dst.clone())); - if auth.links.is_empty() { - to_del = Some(*peer_id); - break; - } - } - if let Some(peer_id) = to_del.take() { - guard.remove(&peer_id); - } - } - - async fn handle_close(&self, peer_id: &ZenohId) { - zasynclock!(self.authenticated).remove(peer_id); - } -} - -//noinspection ALL -impl From> for PeerAuthenticator { - fn from(v: Arc) -> PeerAuthenticator { - PeerAuthenticator(v) - } -} - -impl From for PeerAuthenticator { - fn from(v: UserPasswordAuthenticator) -> PeerAuthenticator { - Self::from(Arc::new(v)) - } -} diff --git a/io/zenoh-transport/src/unicast/establishment/cookie.rs b/io/zenoh-transport/src/unicast/establishment/cookie.rs index e4f8b193de..169fa0f9fb 100644 --- a/io/zenoh-transport/src/unicast/establishment/cookie.rs +++ b/io/zenoh-transport/src/unicast/establishment/cookie.rs @@ -11,85 +11,109 @@ // Contributors: // ZettaScale Zenoh Team, // -use super::properties::EstablishmentProperties; +// use super::properties::EstablishmentProperties; +use crate::unicast::establishment::ext; +use std::convert::TryFrom; use zenoh_buffers::{ reader::{DidntRead, HasReader, Reader}, writer::{DidntWrite, HasWriter, Writer}, }; -use zenoh_codec::{RCodec, WCodec, Zenoh060}; +use zenoh_codec::{RCodec, WCodec, Zenoh080}; use zenoh_crypto::{BlockCipher, PseudoRng}; -use zenoh_protocol::core::{Property, WhatAmI, ZInt, ZenohId}; - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Cookie { - pub whatami: WhatAmI, - pub zid: ZenohId, - pub sn_resolution: ZInt, - pub is_qos: bool, - pub nonce: ZInt, - pub properties: EstablishmentProperties, +use zenoh_protocol::core::{Resolution, WhatAmI, ZenohId}; + +#[derive(Debug, PartialEq)] +pub(crate) struct Cookie { + pub(crate) zid: ZenohId, + pub(crate) whatami: WhatAmI, + pub(crate) resolution: Resolution, + pub(crate) batch_size: u16, + pub(crate) nonce: u64, + // Extensions + pub(crate) ext_qos: ext::qos::StateAccept, + #[cfg(feature = "transport_multilink")] + pub(crate) ext_mlink: ext::multilink::StateAccept, + #[cfg(feature = "shared-memory")] + pub(crate) ext_shm: ext::shm::StateAccept, + #[cfg(feature = "transport_auth")] + pub(crate) ext_auth: ext::auth::StateAccept, } -impl WCodec<&Cookie, &mut W> for Zenoh060 +impl WCodec<&Cookie, &mut W> for Zenoh080 where W: Writer, { type Output = Result<(), DidntWrite>; fn write(self, writer: &mut W, x: &Cookie) -> Self::Output { - let wai: ZInt = x.whatami.into(); - self.write(&mut *writer, wai)?; self.write(&mut *writer, &x.zid)?; - self.write(&mut *writer, x.sn_resolution)?; - let is_qos = u8::from(x.is_qos); - self.write(&mut *writer, is_qos)?; + let wai: u8 = x.whatami.into(); + self.write(&mut *writer, wai)?; + self.write(&mut *writer, x.resolution.as_u8())?; + self.write(&mut *writer, x.batch_size)?; self.write(&mut *writer, x.nonce)?; - self.write(&mut *writer, x.properties.as_slice())?; + // Extensions + self.write(&mut *writer, &x.ext_qos)?; + #[cfg(feature = "transport_multilink")] + self.write(&mut *writer, &x.ext_mlink)?; + #[cfg(feature = "shared-memory")] + self.write(&mut *writer, &x.ext_shm)?; + #[cfg(feature = "transport_auth")] + self.write(&mut *writer, &x.ext_auth)?; Ok(()) } } -impl RCodec for Zenoh060 +impl RCodec for Zenoh080 where R: Reader, { type Error = DidntRead; fn read(self, reader: &mut R) -> Result { - let wai: ZInt = self.read(&mut *reader)?; - let whatami = WhatAmI::try_from(wai).ok_or(DidntRead)?; let zid: ZenohId = self.read(&mut *reader)?; - let sn_resolution: ZInt = self.read(&mut *reader)?; - let is_qos: u8 = self.read(&mut *reader)?; - let is_qos = is_qos == 1; - let nonce: ZInt = self.read(&mut *reader)?; - let mut ps: Vec = self.read(&mut *reader)?; - let mut properties = EstablishmentProperties::new(); - for p in ps.drain(..) { - properties.insert(p).map_err(|_| DidntRead)?; - } + let wai: u8 = self.read(&mut *reader)?; + let whatami = WhatAmI::try_from(wai).map_err(|_| DidntRead)?; + let resolution: u8 = self.read(&mut *reader)?; + let resolution = Resolution::from(resolution); + let batch_size: u16 = self.read(&mut *reader)?; + let nonce: u64 = self.read(&mut *reader)?; + // Extensions + let ext_qos: ext::qos::StateAccept = self.read(&mut *reader)?; + #[cfg(feature = "transport_multilink")] + let ext_mlink: ext::multilink::StateAccept = self.read(&mut *reader)?; + #[cfg(feature = "shared-memory")] + let ext_shm: ext::shm::StateAccept = self.read(&mut *reader)?; + #[cfg(feature = "transport_auth")] + let ext_auth: ext::auth::StateAccept = self.read(&mut *reader)?; let cookie = Cookie { - whatami, zid, - sn_resolution, - is_qos, + whatami, + resolution, + batch_size, nonce, - properties, + ext_qos, + #[cfg(feature = "transport_multilink")] + ext_mlink, + #[cfg(feature = "shared-memory")] + ext_shm, + #[cfg(feature = "transport_auth")] + ext_auth, }; Ok(cookie) } } -pub(super) struct Zenoh060Cookie<'a> { +pub(super) struct Zenoh080Cookie<'a> { pub(super) cipher: &'a BlockCipher, pub(super) prng: &'a mut PseudoRng, - pub(super) codec: Zenoh060, + pub(super) codec: Zenoh080, } -impl WCodec<&Cookie, &mut W> for &mut Zenoh060Cookie<'_> +impl WCodec<&Cookie, &mut W> for &mut Zenoh080Cookie<'_> where W: Writer, { @@ -108,7 +132,7 @@ where } } -impl RCodec for &mut Zenoh060Cookie<'_> +impl RCodec for &mut Zenoh080Cookie<'_> where R: Reader, { @@ -127,18 +151,24 @@ where impl Cookie { #[cfg(test)] - pub fn rand() -> Self { + pub(crate) fn rand() -> Self { use rand::Rng; let mut rng = rand::thread_rng(); Self { - whatami: WhatAmI::rand(), zid: ZenohId::default(), - sn_resolution: rng.gen(), - is_qos: rng.gen_bool(0.5), + whatami: WhatAmI::rand(), + resolution: Resolution::rand(), + batch_size: rng.gen(), nonce: rng.gen(), - properties: EstablishmentProperties::rand(), + ext_qos: ext::qos::StateAccept::rand(), + #[cfg(feature = "transport_multilink")] + ext_mlink: ext::multilink::StateAccept::rand(), + #[cfg(feature = "shared-memory")] + ext_shm: ext::shm::StateAccept::rand(), + #[cfg(feature = "transport_auth")] + ext_auth: ext::auth::StateAccept::rand(), } } } @@ -177,12 +207,12 @@ mod tests { run_single!($type, $rand, $codec, $codec, buffer); println!("ZBuf: codec {}", std::any::type_name::<$type>()); - let mut buffer = ZBuf::default(); + let mut buffer = ZBuf::empty(); run_single!($type, $rand, $codec, $codec, buffer); }; } - let codec = Zenoh060::default(); + let codec = Zenoh080::new(); run!(Cookie, Cookie::rand(), codec); let mut prng = PseudoRng::from_entropy(); @@ -190,10 +220,10 @@ mod tests { prng.fill(&mut key[..]); let cipher = BlockCipher::new(key); - let mut codec = Zenoh060Cookie { + let mut codec = Zenoh080Cookie { prng: &mut prng, cipher: &cipher, - codec: Zenoh060::default(), + codec: Zenoh080::new(), }; run!(Cookie, Cookie::rand(), codec); diff --git a/io/zenoh-transport/src/unicast/establishment/ext/auth/mod.rs b/io/zenoh-transport/src/unicast/establishment/ext/auth/mod.rs new file mode 100644 index 0000000000..0e9c385e46 --- /dev/null +++ b/io/zenoh-transport/src/unicast/establishment/ext/auth/mod.rs @@ -0,0 +1,795 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +#[cfg(feature = "auth_pubkey")] +pub(crate) mod pubkey; +#[cfg(feature = "auth_usrpwd")] +pub(crate) mod usrpwd; + +use crate::unicast::establishment::{AcceptFsm, OpenFsm}; +use async_std::sync::{Mutex, RwLock}; +use async_trait::async_trait; +#[cfg(feature = "auth_pubkey")] +pub use pubkey::*; +use rand::{CryptoRng, Rng}; +use std::convert::TryInto; +use std::marker::PhantomData; +#[cfg(feature = "auth_usrpwd")] +pub use usrpwd::*; +use zenoh_buffers::reader::SiphonableReader; +use zenoh_buffers::ZBuf; +use zenoh_buffers::{ + reader::{DidntRead, HasReader, Reader}, + writer::{DidntWrite, HasWriter, Writer}, +}; +use zenoh_codec::{RCodec, WCodec, Zenoh080}; +use zenoh_config::Config; +use zenoh_core::{bail, zerror, Error as ZError, Result as ZResult}; +use zenoh_crypto::PseudoRng; +use zenoh_protocol::{ + common::{iext, ZExtUnknown}, + transport::{init, open}, +}; + +pub(crate) mod id { + #[cfg(feature = "auth_pubkey")] + pub(crate) const PUBKEY: u8 = 0x1; + #[cfg(feature = "auth_usrpwd")] + pub(crate) const USRPWD: u8 = 0x2; +} + +#[derive(Debug, Default)] +pub struct Auth { + #[cfg(feature = "auth_pubkey")] + pubkey: Option>, + #[cfg(feature = "auth_usrpwd")] + usrpwd: Option>, +} + +impl Auth { + pub(crate) async fn from_config(config: &Config) -> ZResult { + let auth = config.transport().auth(); + + Ok(Self { + #[cfg(feature = "auth_pubkey")] + pubkey: AuthPubKey::from_config(auth.pubkey()) + .await? + .map(RwLock::new), + #[cfg(feature = "auth_usrpwd")] + usrpwd: AuthUsrPwd::from_config(auth.usrpwd()) + .await? + .map(RwLock::new), + }) + } + + pub(crate) fn open(&self, #[allow(unused)] prng: &mut R) -> StateOpen + where + R: Rng + CryptoRng, + { + StateOpen { + #[cfg(feature = "auth_pubkey")] + pubkey: self.pubkey.is_some().then_some(pubkey::StateOpen::new()), + #[cfg(feature = "auth_usrpwd")] + usrpwd: self + .usrpwd + .is_some() + .then_some(usrpwd::StateOpen::new(prng)), + } + } + + pub(crate) fn accept(&self, #[allow(unused)] prng: &mut R) -> StateAccept + where + R: Rng + CryptoRng, + { + StateAccept { + #[cfg(feature = "auth_pubkey")] + pubkey: self.pubkey.is_some().then_some(pubkey::StateAccept::new()), + #[cfg(feature = "auth_usrpwd")] + usrpwd: self + .usrpwd + .is_some() + .then_some(usrpwd::StateAccept::new(prng)), + } + } + + pub(crate) fn fsm<'a>(&'a self, #[allow(unused)] prng: &'a Mutex) -> AuthFsm<'a> { + AuthFsm { + #[cfg(feature = "auth_pubkey")] + pubkey: self.pubkey.as_ref().map(|x| AuthPubKeyFsm::new(x, prng)), + #[cfg(feature = "auth_usrpwd")] + usrpwd: self.usrpwd.as_ref().map(AuthUsrPwdFsm::new), + _a: PhantomData, + } + } +} + +#[cfg(feature = "test")] +impl Auth { + pub const fn empty() -> Self { + Self { + #[cfg(feature = "auth_pubkey")] + pubkey: None, + #[cfg(feature = "auth_usrpwd")] + usrpwd: None, + } + } + + #[cfg(feature = "auth_pubkey")] + pub fn set_pubkey(&mut self, pubkey: Option) { + self.pubkey = pubkey.map(RwLock::new); + } + + #[cfg(feature = "auth_pubkey")] + pub fn get_pubkey(&self) -> Option<&RwLock> { + self.pubkey.as_ref() + } + + #[cfg(feature = "auth_usrpwd")] + pub fn set_usrpwd(&mut self, usrpwd: Option) { + self.usrpwd = usrpwd.map(RwLock::new); + } + + #[cfg(feature = "auth_usrpwd")] + pub fn get_usrpwd(&self) -> Option<&RwLock> { + self.usrpwd.as_ref() + } +} + +pub(crate) struct AuthFsm<'a> { + #[cfg(feature = "auth_pubkey")] + pubkey: Option>, + #[cfg(feature = "auth_usrpwd")] + usrpwd: Option>, + _a: PhantomData<&'a ()>, // Required only when all auth features are disabled +} + +#[derive(Debug, PartialEq, Eq)] +pub(crate) struct StateOpen { + #[cfg(feature = "auth_pubkey")] + pubkey: Option, + #[cfg(feature = "auth_usrpwd")] + usrpwd: Option, +} + +#[derive(Debug, PartialEq)] +pub(crate) struct StateAccept { + #[cfg(feature = "auth_pubkey")] + pubkey: Option, + #[cfg(feature = "auth_usrpwd")] + usrpwd: Option, +} + +impl StateAccept { + #[cfg(test)] + pub(crate) fn rand() -> Self { + let mut rng = rand::thread_rng(); + Self { + #[cfg(feature = "auth_pubkey")] + pubkey: rng.gen_bool(0.5).then_some(pubkey::StateAccept::rand()), + #[cfg(feature = "auth_usrpwd")] + usrpwd: rng.gen_bool(0.5).then_some(usrpwd::StateAccept::rand()), + } + } +} + +// Codec +impl WCodec<&StateAccept, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &StateAccept) -> Self::Output { + let mut count: usize = 0; + let mut buff = vec![]; + let mut wbuf = buff.writer(); + + #[cfg(feature = "auth_pubkey")] + { + if let Some(pubkey) = x.pubkey.as_ref() { + self.write(&mut wbuf, id::PUBKEY)?; + self.write(&mut wbuf, pubkey)?; + count += 1; + } + } + + #[cfg(feature = "auth_usrpwd")] + { + if let Some(usrpwd) = x.usrpwd.as_ref() { + self.write(&mut wbuf, id::USRPWD)?; + self.write(&mut wbuf, usrpwd)?; + count += 1; + } + } + + self.write(&mut *writer, count)?; + if !buff.is_empty() { + let mut rbuf = buff.reader(); + rbuf.siphon(&mut *writer).map_err(|_| DidntWrite)?; + } + + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let mut count: usize = self.read(&mut *reader)?; + + #[cfg(feature = "auth_pubkey")] + let mut pubkey: Option = None; + #[cfg(feature = "auth_usrpwd")] + let mut usrpwd: Option = None; + + while count > 0 { + let e: u8 = self.read(&mut *reader)?; + match e { + #[cfg(feature = "auth_pubkey")] + id::PUBKEY => { + pubkey = Some(self.read(&mut *reader)?); + } + #[cfg(feature = "auth_usrpwd")] + id::USRPWD => { + usrpwd = Some(self.read(&mut *reader)?); + } + _ => return Err(DidntRead), + } + + count -= 1; + } + + let state = StateAccept { + #[cfg(feature = "auth_pubkey")] + pubkey, + #[cfg(feature = "auth_usrpwd")] + usrpwd, + }; + Ok(state) + } +} + +macro_rules! ztryinto { + ($x:expr, $s:expr) => { + if let Some(x) = $x { + Some( + x.try_into() + .map_err(|_| zerror!("{} Decoding error.", $s))?, + ) + } else { + None + } + }; +} + +macro_rules! ztake { + ($x:expr, $id:expr) => { + $x.iter() + .position(|x| x.id & iext::ID_MASK == $id) + .map(|i| $x.remove(i)) + }; +} + +/*************************************/ +/* OPEN */ +/*************************************/ +#[async_trait] +impl<'a> OpenFsm for AuthFsm<'a> { + type Error = ZError; + + type SendInitSynIn = &'a StateOpen; + type SendInitSynOut = Option; + async fn send_init_syn( + &self, + state: Self::SendInitSynIn, + ) -> Result { + const S: &str = "Auth extension - Send InitSyn."; + + let mut exts: Vec = vec![]; + + #[cfg(feature = "auth_pubkey")] + { + match (self.pubkey.as_ref(), state.pubkey.as_ref()) { + (Some(e), Some(s)) => { + if let Some(e) = e.send_init_syn(s).await?.take() { + exts.push(e.into()) + } + } + (None, None) => {} + _ => bail!("{S} Invalid PubKey configuration."), + } + } + + #[cfg(feature = "auth_usrpwd")] + { + match (self.usrpwd.as_ref(), state.usrpwd.as_ref()) { + (Some(e), Some(s)) => { + if let Some(e) = e.send_init_syn(s).await?.take() { + exts.push(e.into()) + } + } + (None, None) => {} + _ => bail!("{S} Invalid UsrPwd configuration."), + } + } + + let codec = Zenoh080::new(); + let mut buff = vec![]; + let mut writer = buff.writer(); + codec + .write(&mut writer, exts.as_slice()) + .map_err(|_| zerror!("{S} Encoding error."))?; + + let output = (!buff.is_empty()).then_some(init::ext::Auth::new(buff.into())); + Ok(output) + } + + type RecvInitAckIn = (&'a mut StateOpen, Option); + type RecvInitAckOut = (); + async fn recv_init_ack( + &self, + input: Self::RecvInitAckIn, + ) -> Result { + const S: &str = "Auth extension - Recv InitAck."; + + let (state, ext) = input; + let ext = ext.unwrap_or(init::ext::Auth::new(ZBuf::empty())); + + let codec = Zenoh080::new(); + let mut reader = ext.value.reader(); + let mut exts: Vec = codec + .read(&mut reader) + .map_err(|_| zerror!("{S} Decoding error."))?; + + #[cfg(feature = "auth_pubkey")] + { + match (self.pubkey.as_ref(), state.pubkey.as_mut()) { + (Some(e), Some(s)) => { + let x = ztake!(exts, id::PUBKEY); + e.recv_init_ack((s, ztryinto!(x, S))).await?; + } + (None, None) => {} + _ => bail!("{S} Invalid PubKey configuration."), + } + } + + #[cfg(feature = "auth_usrpwd")] + { + match (self.usrpwd.as_ref(), state.usrpwd.as_mut()) { + (Some(e), Some(s)) => { + let x = ztake!(exts, id::USRPWD); + e.recv_init_ack((s, ztryinto!(x, S))).await?; + } + (None, None) => {} + _ => bail!("{S} Invalid UsrPwd configuration."), + } + } + + Ok(()) + } + + type SendOpenSynIn = &'a StateOpen; + type SendOpenSynOut = Option; + async fn send_open_syn( + &self, + state: Self::SendOpenSynIn, + ) -> Result { + const S: &str = "Auth extension - Send OpenSyn."; + + let mut exts: Vec = vec![]; + + #[cfg(feature = "auth_pubkey")] + { + match (self.pubkey.as_ref(), state.pubkey.as_ref()) { + (Some(e), Some(s)) => { + if let Some(e) = e.send_open_syn(s).await?.take() { + exts.push(e.into()) + } + } + (None, None) => {} + _ => bail!("{S} Invalid PubKey configuration."), + } + } + + #[cfg(feature = "auth_usrpwd")] + { + match (self.usrpwd.as_ref(), state.usrpwd.as_ref()) { + (Some(e), Some(s)) => { + if let Some(e) = e.send_open_syn(s).await?.take() { + exts.push(e.into()) + } + } + (None, None) => {} + _ => bail!("{S} Invalid UsrPwd configuration."), + } + } + + let codec = Zenoh080::new(); + let mut buff = vec![]; + let mut writer = buff.writer(); + codec + .write(&mut writer, exts.as_slice()) + .map_err(|_| zerror!("{S} Encoding error."))?; + + let output = (!buff.is_empty()).then_some(open::ext::Auth::new(buff.into())); + Ok(output) + } + + type RecvOpenAckIn = (&'a mut StateOpen, Option); + type RecvOpenAckOut = (); + async fn recv_open_ack( + &self, + input: Self::RecvOpenAckIn, + ) -> Result { + const S: &str = "Auth extension - Recv OpenAck."; + + let (state, ext) = input; + let ext = ext.unwrap_or(init::ext::Auth::new(ZBuf::empty())); + + let codec = Zenoh080::new(); + let mut reader = ext.value.reader(); + let mut exts: Vec = codec + .read(&mut reader) + .map_err(|_| zerror!("{S} Decoding error."))?; + + #[cfg(feature = "auth_pubkey")] + { + match (self.pubkey.as_ref(), state.pubkey.as_mut()) { + (Some(e), Some(s)) => { + let x = ztake!(exts, id::PUBKEY); + e.recv_open_ack((s, ztryinto!(x, S))).await?; + } + (None, None) => {} + _ => bail!("{S} Invalid PubKey configuration."), + } + } + + #[cfg(feature = "auth_usrpwd")] + { + match (self.usrpwd.as_ref(), state.usrpwd.as_mut()) { + (Some(e), Some(s)) => { + let x = ztake!(exts, id::USRPWD); + e.recv_open_ack((s, ztryinto!(x, S))).await?; + } + (None, None) => {} + _ => bail!("{S} Invalid UsrPwd configuration."), + } + } + + Ok(()) + } +} + +/*************************************/ +/* ACCEPT */ +/*************************************/ +#[async_trait] +impl<'a> AcceptFsm for AuthFsm<'a> { + type Error = ZError; + + type RecvInitSynIn = (&'a mut StateAccept, Option); + type RecvInitSynOut = (); + async fn recv_init_syn( + &self, + input: Self::RecvInitSynIn, + ) -> Result { + const S: &str = "Auth extension - Recv InitSyn."; + + let (state, ext) = input; + let ext = ext.unwrap_or(init::ext::Auth::new(ZBuf::empty())); + + let codec = Zenoh080::new(); + let mut reader = ext.value.reader(); + let mut exts: Vec = codec + .read(&mut reader) + .map_err(|_| zerror!("{S} Decoding error."))?; + + #[cfg(feature = "auth_pubkey")] + { + match (self.pubkey.as_ref(), state.pubkey.as_mut()) { + (Some(e), Some(s)) => { + let x = ztake!(exts, id::PUBKEY); + e.recv_init_syn((s, ztryinto!(x, S))).await?; + } + (None, None) => {} + _ => bail!("{S} Invalid PubKey configuration."), + } + } + + #[cfg(feature = "auth_usrpwd")] + { + match (self.usrpwd.as_ref(), state.usrpwd.as_mut()) { + (Some(e), Some(s)) => { + let x = ztake!(exts, id::USRPWD); + e.recv_init_syn((s, ztryinto!(x, S))).await?; + } + (None, None) => {} + _ => bail!("{S} Invalid UsrPwd configuration."), + } + } + + Ok(()) + } + + type SendInitAckIn = &'a StateAccept; + type SendInitAckOut = Option; + async fn send_init_ack( + &self, + state: Self::SendInitAckIn, + ) -> Result { + const S: &str = "Auth extension - Send InitAck."; + + let mut exts: Vec = vec![]; + + #[cfg(feature = "auth_pubkey")] + { + match (self.pubkey.as_ref(), state.pubkey.as_ref()) { + (Some(e), Some(s)) => { + if let Some(e) = e.send_init_ack(s).await?.take() { + exts.push(e.into()) + } + } + (None, None) => {} + _ => bail!("{S} Invalid PubKey configuration."), + } + } + + #[cfg(feature = "auth_usrpwd")] + { + match (self.usrpwd.as_ref(), state.usrpwd.as_ref()) { + (Some(e), Some(s)) => { + if let Some(e) = e.send_init_ack(s).await?.take() { + exts.push(e.into()) + } + } + (None, None) => {} + _ => bail!("{S} Invalid UsrPwd configuration."), + } + } + + let codec = Zenoh080::new(); + let mut buff = vec![]; + let mut writer = buff.writer(); + codec + .write(&mut writer, exts.as_slice()) + .map_err(|_| zerror!("{S} Encoding error."))?; + + let output = (!buff.is_empty()).then_some(init::ext::Auth::new(buff.into())); + Ok(output) + } + + type RecvOpenSynIn = (&'a mut StateAccept, Option); + type RecvOpenSynOut = (); + async fn recv_open_syn( + &self, + input: Self::RecvOpenSynIn, + ) -> Result { + const S: &str = "Auth extension - Recv OpenSyn."; + + let (state, ext) = input; + let ext = ext.unwrap_or(init::ext::Auth::new(ZBuf::empty())); + + let codec = Zenoh080::new(); + let mut reader = ext.value.reader(); + let mut exts: Vec = codec + .read(&mut reader) + .map_err(|_| zerror!("{S} Decoding error."))?; + + #[cfg(feature = "auth_pubkey")] + { + match (self.pubkey.as_ref(), state.pubkey.as_mut()) { + (Some(e), Some(s)) => { + let x = ztake!(exts, id::PUBKEY); + e.recv_open_syn((s, ztryinto!(x, S))).await?; + } + (None, None) => {} + _ => bail!("{S} Invalid PubKey configuration."), + } + } + + #[cfg(feature = "auth_usrpwd")] + { + match (self.usrpwd.as_ref(), state.usrpwd.as_mut()) { + (Some(e), Some(s)) => { + let x = ztake!(exts, id::USRPWD); + e.recv_open_syn((s, ztryinto!(x, S))).await?; + } + (None, None) => {} + _ => bail!("{S} Invalid UsrPwd configuration."), + } + } + + Ok(()) + } + + type SendOpenAckIn = &'a StateAccept; + type SendOpenAckOut = Option; + async fn send_open_ack( + &self, + state: Self::SendOpenAckIn, + ) -> Result { + const S: &str = "Auth extension - Send OpenAck."; + + let mut exts: Vec = vec![]; + + #[cfg(feature = "auth_pubkey")] + { + match (self.pubkey.as_ref(), state.pubkey.as_ref()) { + (Some(e), Some(s)) => { + if let Some(e) = e.send_open_ack(s).await?.take() { + exts.push(e.into()) + } + } + (None, None) => {} + _ => bail!("{S} Invalid PubKey configuration."), + } + } + + #[cfg(feature = "auth_usrpwd")] + { + match (self.usrpwd.as_ref(), state.usrpwd.as_ref()) { + (Some(e), Some(s)) => { + if let Some(e) = e.send_open_ack(s).await?.take() { + exts.push(e.into()) + } + } + (None, None) => {} + _ => bail!("{S} Invalid UsrPwd configuration."), + } + } + + let codec = Zenoh080::new(); + let mut buff = vec![]; + let mut writer = buff.writer(); + codec + .write(&mut writer, exts.as_slice()) + .map_err(|_| zerror!("{S} Encoding error."))?; + + let output = (!buff.is_empty()).then_some(open::ext::Auth::new(buff.into())); + Ok(output) + } +} + +// #[derive(Clone)] +// pub struct TransportAuthenticator(Arc); + +// impl TransportAuthenticator { +// pub async fn from_config(_config: &Config) -> ZResult> { +// #[allow(unused_mut)] +// let mut pas = HashSet::new(); + +// #[cfg(feature = "auth_pubkey")] +// { +// let mut res = PubKeyAuthenticator::from_config(_config).await?; +// if let Some(pa) = res.take() { +// pas.insert(pa.into()); +// } +// } + +// #[cfg(feature = "auth_usrpwd")] +// { +// let mut res = UserPasswordAuthenticator::from_config(_config).await?; +// if let Some(pa) = res.take() { +// pas.insert(pa.into()); +// } +// } + +// Ok(pas) +// } +// } + +/*************************************/ +/* ACCEPT */ +/*************************************/ + +// Return the attachment to be included in the InitSyn message. +// +// # Arguments +// * `link` - The [`AuthenticatedPeerLink`][AuthenticatedPeerLink] the initial InitSyn message will be sent on +// +// * `node_id` - The [`ZenohId`][ZenohId] of the sender of the InitSyn, i.e., the peer +// initiating a new transport. +// +// async fn get_init_syn_properties( +// &self, +// link: &AuthenticatedLink, +// node_id: &ZenohId, +// ) -> ZResult>>; + +// Return the attachment to be included in the InitAck message to be sent +// in response of the authenticated InitSyn. +// +// # Arguments +// * `link` - The [`AuthenticatedPeerLink`][AuthenticatedPeerLink] the InitSyn message was received on +// +// * `cookie` - The Cookie containing the internal state +// +// * `property` - The optional `Property` included in the InitSyn message +// +// async fn handle_init_syn( +// &self, +// link: &AuthenticatedLink, +// cookie: &Cookie, +// property: Option>, +// ) -> ZResult<(Option>, Option>)>; // (Attachment, Cookie) + +// Return the attachment to be included in the OpenSyn message to be sent +// in response of the authenticated InitAck. +// +// # Arguments +// * `link` - The [`AuthenticatedPeerLink`][AuthenticatedPeerLink] the InitSyn message was received on +// +// * `node_id` - The [`ZenohId`][ZenohId] of the sender of the InitAck message +// +// * `sn_resolution` - The sn_resolution negotiated by the sender of the InitAck message +// +// * `properties` - The optional `Property` included in the InitAck message +// +// async fn handle_init_ack( +// &self, +// link: &AuthenticatedLink, +// node_id: &ZenohId, +// sn_resolution: u64, +// property: Option>, +// ) -> ZResult>>; + +// Return the attachment to be included in the OpenAck message to be sent +// in response of the authenticated OpenSyn. +// +// # Arguments +// * `link` - The [`AuthenticatedPeerLink`][AuthenticatedPeerLink] the OpenSyn message was received on +// +// * `properties` - The optional `Property` included in the OpenSyn message +// +// * `cookie` - The optional `Property` included in the OpenSyn message +// +// async fn handle_open_syn( +// &self, +// link: &AuthenticatedLink, +// cookie: &Cookie, +// property: (Option>, Option>), // (Attachment, Cookie) +// ) -> ZResult>>; + +// Auhtenticate the OpenAck. No message is sent back in response to an OpenAck +// +// # Arguments +// * `link` - The [`AuthenticatedPeerLink`][AuthenticatedPeerLink] the OpenAck message was received on +// +// * `properties` - The optional `Property` included in the OpenAck message +// +// async fn handle_open_ack( +// &self, +// link: &AuthenticatedLink, +// property: Option>, +// ) -> ZResult>>; + +// Handle any error on a link. This callback is mainly used to clean-up any internal state +// of the authenticator in such a way no unnecessary data is left around +// +// # Arguments +// * `link` - The [`AuthenticatedPeerLink`][AuthenticatedPeerLink] generating the error +// +// async fn handle_link_err(&self, link: &AuthenticatedLink); + +// Handle any error on a link. This callback is mainly used to clean-up any internal state +// of the authenticator in such a way no unnecessary data is left around +// +// # Arguments +// * `peerd_id` - The [`ZenohId`][ZenohId] of the transport being closed. +// +// async fn handle_close(&self, node_id: &ZenohId); +// } diff --git a/io/zenoh-transport/src/unicast/establishment/ext/auth/pubkey.rs b/io/zenoh-transport/src/unicast/establishment/ext/auth/pubkey.rs new file mode 100644 index 0000000000..edec665ecc --- /dev/null +++ b/io/zenoh-transport/src/unicast/establishment/ext/auth/pubkey.rs @@ -0,0 +1,656 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::unicast::establishment::{ext::auth::id, AcceptFsm, OpenFsm}; +use async_std::sync::{Mutex, RwLock}; +use async_trait::async_trait; +use rand::Rng; +use rsa::{ + pkcs1::{DecodeRsaPrivateKey, DecodeRsaPublicKey}, + BigUint, Pkcs1v15Encrypt, PublicKey, PublicKeyParts, RsaPrivateKey, RsaPublicKey, +}; +use std::{collections::HashSet, fmt, ops::Deref, path::Path}; +use zenoh_buffers::{ + reader::{DidntRead, HasReader, Reader}, + writer::{DidntWrite, HasWriter, Writer}, +}; +use zenoh_codec::{RCodec, WCodec, Zenoh080}; +use zenoh_config::PubKeyConf; +use zenoh_core::{bail, zasynclock, zasyncread, zerror, Error as ZError, Result as ZResult}; +use zenoh_crypto::PseudoRng; +use zenoh_protocol::common::{ZExtUnit, ZExtZBuf}; + +mod ext { + use super::{id::PUBKEY, ZExtUnit, ZExtZBuf}; + use zenoh_protocol::{zextunit, zextzbuf}; + + pub(super) type InitSyn = zextzbuf!(PUBKEY, false); + pub(super) type InitAck = zextzbuf!(PUBKEY, false); + pub(super) type OpenSyn = zextzbuf!(PUBKEY, false); + pub(super) type OpenAck = zextunit!(PUBKEY, false); +} + +// Authenticator +#[derive(Debug)] +pub struct AuthPubKey { + lookup: Option>, + pub_key: ZPublicKey, + pri_key: ZPrivateKey, +} + +impl AuthPubKey { + pub fn new(pub_key: ZPublicKey, pri_key: ZPrivateKey) -> Self { + Self { + lookup: Some(HashSet::new()), + pub_key, + pri_key, + } + } + + pub(crate) fn disable_lookup(&mut self) { + self.lookup = None; + } + + pub async fn add_pubkey(&mut self, pub_key: ZPublicKey) -> ZResult<()> { + if let Some(lookup) = self.lookup.as_mut() { + lookup.insert(pub_key); + } + Ok(()) + } + + pub async fn del_pubkey(&mut self, pub_key: &ZPublicKey) -> ZResult<()> { + if let Some(lookup) = self.lookup.as_mut() { + lookup.remove(pub_key); + } + Ok(()) + } + + pub async fn from_config(config: &PubKeyConf) -> ZResult> { + const S: &str = "PubKey extension - From config."; + + // First, check if PEM keys are provided + match (config.public_key_pem(), config.private_key_pem()) { + (Some(public), Some(private)) => { + let pub_key = RsaPublicKey::from_pkcs1_pem(public) + .map_err(|e| zerror!("{} Rsa Public Key: {}.", S, e))?; + let pri_key = RsaPrivateKey::from_pkcs1_pem(private) + .map_err(|e| zerror!("{} Rsa Private Key: {}.", S, e))?; + return Ok(Some(Self::new(pub_key.into(), pri_key.into()))); + } + (Some(_), None) => { + bail!("{S} Missing Rsa Private Key: PEM.") + } + (None, Some(_)) => { + bail!("{S} Missing Rsa Public Key: PEM.") + } + (None, None) => {} + } + + // Second, check if PEM files are provided + match (config.public_key_file(), config.private_key_file()) { + (Some(public), Some(private)) => { + let path = Path::new(public); + let pub_key = RsaPublicKey::read_pkcs1_pem_file(path) + .map_err(|e| zerror!("{} Rsa Public Key: {}.", S, e))?; + let path = Path::new(private); + let pri_key = RsaPrivateKey::read_pkcs1_pem_file(path) + .map_err(|e| zerror!("{} Rsa Private Key: {}.", S, e))?; + return Ok(Some(Self::new(pub_key.into(), pri_key.into()))); + } + (Some(_), None) => { + bail!("{S} Missing Rsa Private Key: file.") + } + (None, Some(_)) => { + bail!("{S} Missing Rsa Public Key: file.") + } + (None, None) => {} + } + + // @TODO: populate lookup file + + Ok(None) + } +} + +#[repr(transparent)] +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct ZPublicKey(RsaPublicKey); + +impl Deref for ZPublicKey { + type Target = RsaPublicKey; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl fmt::Debug for ZPublicKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for b in self.0.n().to_bytes_le() { + write!(f, "{:02x}", b)?; + } + for b in self.0.e().to_bytes_le() { + write!(f, "{:02x}", b)?; + } + Ok(()) + } +} + +impl From for ZPublicKey { + fn from(x: RsaPublicKey) -> Self { + Self(x) + } +} + +#[repr(transparent)] +#[derive(Clone, PartialEq, Eq)] +pub struct ZPrivateKey(RsaPrivateKey); + +impl Deref for ZPrivateKey { + type Target = RsaPrivateKey; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl fmt::Debug for ZPrivateKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "***",) + } +} + +impl From for ZPrivateKey { + fn from(x: RsaPrivateKey) -> Self { + Self(x) + } +} + +impl WCodec<&ZPublicKey, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &ZPublicKey) -> Self::Output { + self.write(&mut *writer, x.n().to_bytes_le().as_slice())?; + self.write(&mut *writer, x.e().to_bytes_le().as_slice())?; + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let n: Vec = self.read(&mut *reader)?; + let n = BigUint::from_bytes_le(n.as_slice()); + let e: Vec = self.read(&mut *reader)?; + let e = BigUint::from_bytes_le(e.as_slice()); + let rsa = RsaPublicKey::new(n, e).map_err(|_| DidntRead)?; + + Ok(ZPublicKey(rsa)) + } +} + +/*************************************/ +/* InitSyn */ +/*************************************/ +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// ~ public key ~ +/// +---------------+ +/// +/// ZExtZBuf +pub(crate) struct InitSyn { + pub(crate) alice_pubkey: ZPublicKey, +} + +impl WCodec<&InitSyn, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &InitSyn) -> Self::Output { + self.write(&mut *writer, &x.alice_pubkey)?; + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let alice_pubkey: ZPublicKey = self.read(&mut *reader)?; + Ok(InitSyn { alice_pubkey }) + } +} + +/*************************************/ +/* InitAck */ +/*************************************/ +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// ~ public key ~ +/// +---------------+ +/// ~ ciphered nonce~ +/// +---------------+ +/// +/// ZExtZBuf +pub(crate) struct InitAck { + pub(crate) bob_pubkey: ZPublicKey, + pub(crate) nonce_encrypted_with_alice_pubkey: Vec, +} + +impl WCodec<&InitAck, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &InitAck) -> Self::Output { + self.write(&mut *writer, &x.bob_pubkey)?; + self.write(&mut *writer, x.nonce_encrypted_with_alice_pubkey.as_slice())?; + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let bob_pubkey: ZPublicKey = self.read(&mut *reader)?; + let nonce_encrypted_with_alice_pubkey: Vec = self.read(&mut *reader)?; + Ok(InitAck { + bob_pubkey, + nonce_encrypted_with_alice_pubkey, + }) + } +} + +/*************************************/ +/* OpenSyn */ +/*************************************/ +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// ~ ciphered nonce~ +/// +---------------+ +/// +/// ZExtZBuf +pub(crate) struct OpenSyn { + pub(crate) nonce_encrypted_with_bob_pubkey: Vec, +} + +impl WCodec<&OpenSyn, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &OpenSyn) -> Self::Output { + self.write(&mut *writer, x.nonce_encrypted_with_bob_pubkey.as_slice())?; + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let nonce_encrypted_with_bob_pubkey: Vec = self.read(&mut *reader)?; + Ok(OpenSyn { + nonce_encrypted_with_bob_pubkey, + }) + } +} + +/*************************************/ +/* OpenAck */ +/*************************************/ +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// +---------------+ +/// +/// ZExtUnit + +pub(crate) struct AuthPubKeyFsm<'a> { + inner: &'a RwLock, + prng: &'a Mutex, +} + +impl<'a> AuthPubKeyFsm<'a> { + pub(crate) const fn new(inner: &'a RwLock, prng: &'a Mutex) -> Self { + Self { inner, prng } + } +} + +/*************************************/ +/* OPEN */ +/*************************************/ +#[derive(Debug, PartialEq, Eq)] +pub(crate) struct StateOpen { + nonce: Vec, +} + +impl StateOpen { + pub(crate) const fn new() -> Self { + Self { nonce: vec![] } + } +} + +#[async_trait] +impl<'a> OpenFsm for AuthPubKeyFsm<'a> { + type Error = ZError; + + type SendInitSynIn = &'a StateOpen; + type SendInitSynOut = Option; + async fn send_init_syn( + &self, + _input: Self::SendInitSynIn, + ) -> Result { + const S: &str = "PubKey extension - Send InitSyn."; + log::trace!("{S}"); + + let init_syn = InitSyn { + alice_pubkey: zasyncread!(self.inner).pub_key.clone(), + }; + + let codec = Zenoh080::new(); + let mut buff = vec![]; + let mut writer = buff.writer(); + codec + .write(&mut writer, &init_syn) + .map_err(|_| zerror!("{S} Encoding error."))?; + + Ok(Some(ZExtZBuf::new(buff.into()))) + } + + type RecvInitAckIn = (&'a mut StateOpen, Option); + type RecvInitAckOut = (); + async fn recv_init_ack( + &self, + input: Self::RecvInitAckIn, + ) -> Result { + const S: &str = "PubKey extension - Recv InitAck."; + log::trace!("{S}"); + + let (state, mut ext) = input; + + let ext = ext + .take() + .ok_or_else(|| zerror!("{S} Missing PubKey extension."))?; + + let codec = Zenoh080::new(); + let mut reader = ext.value.reader(); + let init_ack: InitAck = codec + .read(&mut reader) + .map_err(|_| zerror!("{S} Decoding error."))?; + + let r_inner = zasyncread!(self.inner); + if let Some(lookup) = r_inner.lookup.as_ref() { + if !lookup.is_empty() && !lookup.contains(&init_ack.bob_pubkey) { + bail!("{S} Unauthorized PubKey."); + } + } + + let mut prng = zasynclock!(self.prng); + let nonce = r_inner + .pri_key + .decrypt_blinded( + &mut *prng, + Pkcs1v15Encrypt, + init_ack.nonce_encrypted_with_alice_pubkey.as_slice(), + ) + .map_err(|_| zerror!("{S} Decryption error."))?; + drop(r_inner); + + state.nonce = init_ack + .bob_pubkey + .encrypt(&mut *prng, Pkcs1v15Encrypt, nonce.as_slice())?; + + Ok(()) + } + + type SendOpenSynIn = &'a StateOpen; + type SendOpenSynOut = Option; + async fn send_open_syn( + &self, + state: Self::SendOpenSynIn, + ) -> Result { + const S: &str = "PubKey extension - Send OpenSyn."; + log::trace!("{S}"); + + let open_syn = OpenSyn { + nonce_encrypted_with_bob_pubkey: state.nonce.clone(), + }; + + let codec = Zenoh080::new(); + let mut buff = vec![]; + let mut writer = buff.writer(); + codec + .write(&mut writer, &open_syn) + .map_err(|_| zerror!("{S} Encoding error."))?; + + Ok(Some(ZExtZBuf::new(buff.into()))) + } + + type RecvOpenAckIn = (&'a mut StateOpen, Option); + type RecvOpenAckOut = (); + async fn recv_open_ack( + &self, + input: Self::RecvOpenAckIn, + ) -> Result { + const S: &str = "PubKey extension - Recv OpenAck."; + + let (_, ext) = input; + if ext.is_none() { + bail!("{S} Expected extension."); + } + + Ok(()) + } +} + +/*************************************/ +/* ACCEPT */ +/*************************************/ + +#[derive(Debug)] +pub(crate) struct StateAccept { + nonce: Vec, + challenge: u64, +} + +impl StateAccept { + pub(crate) const fn new() -> Self { + Self { + nonce: vec![], + challenge: 0, + } + } + + #[cfg(all(test, feature = "test"))] + pub(crate) fn rand() -> Self { + let mut rng = rand::thread_rng(); + let mut nonce = vec![0u8; rng.gen_range(0..=64)]; + rng.fill(&mut nonce[..]); + Self { + nonce, + challenge: rng.gen(), + } + } +} + +// Codec +impl WCodec<&StateAccept, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &StateAccept) -> Self::Output { + self.write(&mut *writer, x.challenge) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let challenge: u64 = self.read(&mut *reader)?; + Ok(StateAccept { + nonce: vec![], + challenge, + }) + } +} + +impl PartialEq for StateAccept { + fn eq(&self, other: &Self) -> bool { + self.challenge == other.challenge + } +} + +#[async_trait] +impl<'a> AcceptFsm for AuthPubKeyFsm<'a> { + type Error = ZError; + + type RecvInitSynIn = (&'a mut StateAccept, Option); + type RecvInitSynOut = (); + async fn recv_init_syn( + &self, + input: Self::RecvInitSynIn, + ) -> Result { + const S: &str = "PubKey extension - Recv InitSyn."; + log::trace!("{S}"); + + let (state, mut ext) = input; + + let ext = ext + .take() + .ok_or_else(|| zerror!("{S} Missing PubKey extension."))?; + + let codec = Zenoh080::new(); + let mut reader = ext.value.reader(); + let init_syn: InitSyn = codec + .read(&mut reader) + .map_err(|_| zerror!("{S} Decoding error."))?; + + let r_inner = zasyncread!(self.inner); + if let Some(lookup) = r_inner.lookup.as_ref() { + if !lookup.contains(&init_syn.alice_pubkey) { + bail!("{S} Unauthorized PubKey."); + } + } + + let mut prng = zasynclock!(self.prng); + state.challenge = prng.gen(); + state.nonce = init_syn + .alice_pubkey + .encrypt(&mut *prng, Pkcs1v15Encrypt, &state.challenge.to_le_bytes()) + .map_err(|_| zerror!("{S} Encoding error."))?; + + Ok(()) + } + + type SendInitAckIn = &'a StateAccept; + type SendInitAckOut = Option; + async fn send_init_ack( + &self, + state: Self::SendInitAckIn, + ) -> Result { + const S: &str = "PubKey extension - Send InitAck."; + log::trace!("{S}"); + + let init_ack = InitAck { + bob_pubkey: zasyncread!(self.inner).pub_key.clone(), + nonce_encrypted_with_alice_pubkey: state.nonce.clone(), + }; + + let codec = Zenoh080::new(); + let mut buff = vec![]; + let mut writer = buff.writer(); + codec + .write(&mut writer, &init_ack) + .map_err(|_| zerror!("{S} Encoding error."))?; + + Ok(Some(ZExtZBuf::new(buff.into()))) + } + + type RecvOpenSynIn = (&'a mut StateAccept, Option); + type RecvOpenSynOut = (); + async fn recv_open_syn( + &self, + input: Self::RecvOpenSynIn, + ) -> Result { + const S: &str = "PubKey extension - Recv OpenSyn."; + log::trace!("{S}"); + + let (state, mut ext) = input; + + let ext = ext + .take() + .ok_or_else(|| zerror!("{S} Missing PubKey extension."))?; + + let codec = Zenoh080::new(); + let mut reader = ext.value.reader(); + let open_syn: OpenSyn = codec + .read(&mut reader) + .map_err(|_| zerror!("{S} Decoding error."))?; + + let mut prng = zasynclock!(self.prng); + let nonce = zasyncread!(self.inner) + .pri_key + .decrypt_blinded( + &mut *prng, + Pkcs1v15Encrypt, + open_syn.nonce_encrypted_with_bob_pubkey.as_slice(), + ) + .map_err(|_| zerror!("{S} Decryption error."))?; + + if nonce.as_slice() != state.challenge.to_le_bytes() { + println!("{:02x?}\n{:02x?}", nonce, state.nonce); + bail!("{S} Invalid nonce."); + } + + Ok(()) + } + + type SendOpenAckIn = &'a StateAccept; + type SendOpenAckOut = Option; + async fn send_open_ack( + &self, + _input: Self::SendOpenAckIn, + ) -> Result { + const S: &str = "PubKey extension - Send OpenAck."; + log::trace!("{S}"); + + Ok(Some(ZExtUnit::new())) + } +} diff --git a/io/zenoh-transport/src/unicast/establishment/ext/auth/usrpwd.rs b/io/zenoh-transport/src/unicast/establishment/ext/auth/usrpwd.rs new file mode 100644 index 0000000000..48d292cd32 --- /dev/null +++ b/io/zenoh-transport/src/unicast/establishment/ext/auth/usrpwd.rs @@ -0,0 +1,430 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::unicast::establishment::{ext::auth::id, AcceptFsm, OpenFsm}; +use async_std::{fs, sync::RwLock}; +use async_trait::async_trait; +use rand::{CryptoRng, Rng}; +use std::{collections::HashMap, fmt}; +use zenoh_buffers::{ + reader::{DidntRead, HasReader, Reader}, + writer::{DidntWrite, HasWriter, Writer}, +}; +use zenoh_codec::{RCodec, WCodec, Zenoh080}; +use zenoh_collections::Properties; +use zenoh_config::UsrPwdConf; +use zenoh_core::{bail, zasyncread, zerror, Error as ZError, Result as ZResult}; +use zenoh_crypto::hmac; +use zenoh_protocol::common::{ZExtUnit, ZExtZ64, ZExtZBuf}; + +mod ext { + use super::{id::USRPWD, ZExtUnit, ZExtZ64, ZExtZBuf}; + use zenoh_protocol::{zextunit, zextz64, zextzbuf}; + + pub(super) type InitSyn = zextunit!(USRPWD, false); + pub(super) type InitAck = zextz64!(USRPWD, false); + pub(super) type OpenSyn = zextzbuf!(USRPWD, false); + pub(super) type OpenAck = zextunit!(USRPWD, false); +} + +// Authenticator +type User = Vec; +type Password = Vec; + +pub struct AuthUsrPwd { + lookup: HashMap, + credentials: Option<(User, Password)>, +} + +impl AuthUsrPwd { + pub fn new(credentials: Option<(User, Password)>) -> Self { + Self { + lookup: HashMap::new(), + credentials, + } + } + + pub async fn add_user(&mut self, user: User, password: Password) -> ZResult<()> { + self.lookup.insert(user, password); + Ok(()) + } + + pub async fn del_user(&mut self, user: &User) -> ZResult<()> { + self.lookup.remove(user); + Ok(()) + } + + pub async fn from_config(config: &UsrPwdConf) -> ZResult> { + const S: &str = "UsrPwd extension - From config."; + + let mut lookup: HashMap = HashMap::new(); + if let Some(dict) = config.dictionary_file() { + let content = fs::read_to_string(dict) + .await + .map_err(|e| zerror!("{S} Invalid user-password dictionary file: {}.", e))?; + + // Populate the user-password dictionary + let mut ps = Properties::from(content); + for (user, password) in ps.drain() { + lookup.insert(user.as_bytes().to_owned(), password.as_bytes().to_owned()); + } + log::debug!("{S} User-password dictionary has been configured."); + } + + let mut credentials: Option<(User, Password)> = None; + if let Some(user) = config.user() { + if let Some(password) = config.password() { + log::debug!("{S} User-password has been configured."); + credentials = Some((user.as_bytes().to_owned(), password.as_bytes().to_owned())); + } + } + + if !lookup.is_empty() || credentials.is_some() { + log::debug!("{S} User-password authentication is enabled."); + Ok(Some(Self { + lookup, + credentials, + })) + } else { + Ok(None) + } + } +} + +impl fmt::Debug for AuthUsrPwd { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.credentials.as_ref() { + Some(c) => write!( + f, + "User: '{}', Password: '***'", + String::from_utf8_lossy(&c.0) + )?, + None => write!(f, "User: '', Password: ''")?, + } + write!(f, "Dictionary: {{")?; + for (i, (u, _)) in self.lookup.iter().enumerate() { + if i != 0 { + write!(f, ",")?; + } + write!(f, " {}", String::from_utf8_lossy(u))?; + } + write!(f, " }}") + } +} + +// OpenFsm / AcceptFsm +#[derive(Debug, PartialEq, Eq)] +pub(crate) struct StateOpen { + nonce: u64, +} + +impl StateOpen { + pub(crate) fn new(prng: &mut R) -> Self + where + R: Rng + CryptoRng, + { + Self { nonce: prng.gen() } + } +} + +#[derive(Debug, PartialEq, Eq)] +pub(crate) struct StateAccept { + nonce: u64, +} + +impl StateAccept { + pub(crate) fn new(prng: &mut R) -> Self + where + R: Rng + CryptoRng, + { + Self { nonce: prng.gen() } + } + + #[cfg(all(test, feature = "test"))] + pub(crate) fn rand() -> Self { + let mut rng = rand::thread_rng(); + Self::new(&mut rng) + } +} + +// Codec +impl WCodec<&StateAccept, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &StateAccept) -> Self::Output { + self.write(&mut *writer, x.nonce) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let nonce: u64 = self.read(&mut *reader)?; + Ok(StateAccept { nonce }) + } +} + +pub(crate) struct AuthUsrPwdFsm<'a> { + inner: &'a RwLock, +} + +impl<'a> AuthUsrPwdFsm<'a> { + pub(super) const fn new(inner: &'a RwLock) -> Self { + Self { inner } + } +} + +/*************************************/ +/* InitSyn */ +/*************************************/ +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// +---------------+ +/// +/// ZExtUnit + +/*************************************/ +/* InitAck */ +/*************************************/ +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// ~ nonce ~ +/// +---------------+ +/// +/// ZExtZ64 + +/*************************************/ +/* OpenSyn */ +/*************************************/ +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// ~ user ~ +/// +---------------+ +/// ~ hash ~ +/// +---------------+ +/// +/// ZExtZBuf +struct OpenSyn { + user: Vec, + hmac: Vec, +} + +impl WCodec<&OpenSyn, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &OpenSyn) -> Self::Output { + self.write(&mut *writer, x.user.as_slice())?; + self.write(&mut *writer, x.hmac.as_slice())?; + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let user: Vec = self.read(&mut *reader)?; + let hmac: Vec = self.read(&mut *reader)?; + Ok(OpenSyn { user, hmac }) + } +} + +/*************************************/ +/* OpenAck */ +/*************************************/ +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// +---------------+ +/// +/// ZExtUnit + +#[async_trait] +impl<'a> OpenFsm for AuthUsrPwdFsm<'a> { + type Error = ZError; + + type SendInitSynIn = &'a StateOpen; + type SendInitSynOut = Option; + async fn send_init_syn( + &self, + _input: Self::SendInitSynIn, + ) -> Result { + let output = zasyncread!(self.inner) + .credentials + .is_some() + .then_some(ZExtUnit::new()); + Ok(output) + } + + type RecvInitAckIn = (&'a mut StateOpen, Option); + type RecvInitAckOut = (); + async fn recv_init_ack( + &self, + input: Self::RecvInitAckIn, + ) -> Result { + const S: &str = "UsrPwd extension - Recv InitSyn."; + + if zasyncread!(self.inner).credentials.is_none() { + return Ok(()); + }; + + let (state, mut ext_userwpd) = input; + let ext_usrpwd = ext_userwpd + .take() + .ok_or_else(|| zerror!("{S} Decoding error."))?; + state.nonce = ext_usrpwd.value; + + Ok(()) + } + + type SendOpenSynIn = &'a StateOpen; + type SendOpenSynOut = Option; + async fn send_open_syn( + &self, + state: Self::SendOpenSynIn, + ) -> Result { + const S: &str = "UsrPwd extension - Send OpenSyn."; + + // If credentials are not configured, don't continue the USRPWD authentication + let r_inner = zasyncread!(self.inner); + let (user, password) = match r_inner.credentials.as_ref() { + Some(cr) => cr, + None => return Ok(None), + }; + + // Create the HMAC of the password using the nonce received as a key (it's a challenge) + let key = state.nonce.to_le_bytes(); + let hmac = hmac::sign(&key, password).map_err(|_| zerror!("{S} Encoding error."))?; + // Create the OpenSyn extension + let open_syn = OpenSyn { + user: user.to_vec(), + hmac, + }; + drop(r_inner); + + let codec = Zenoh080::new(); + let mut buff = vec![]; + let mut writer = buff.writer(); + codec + .write(&mut writer, &open_syn) + .map_err(|_| zerror!("{S} Encoding error."))?; + + let output = Some(ZExtZBuf::new(buff.into())); + Ok(output) + } + + type RecvOpenAckIn = (&'a mut StateOpen, Option); + type RecvOpenAckOut = (); + async fn recv_open_ack( + &self, + input: Self::RecvOpenAckIn, + ) -> Result { + const S: &str = "UsrPwd extension - Recv OpenAck."; + + let (_, ext) = input; + if zasyncread!(self.inner).credentials.is_some() && ext.is_none() { + bail!("{S} Expected extension."); + } + + Ok(()) + } +} + +/*************************************/ +/* ACCEPT */ +/*************************************/ +#[async_trait] +impl<'a> AcceptFsm for AuthUsrPwdFsm<'a> { + type Error = ZError; + + type RecvInitSynIn = (&'a mut StateAccept, Option); + type RecvInitSynOut = (); + async fn recv_init_syn( + &self, + input: Self::RecvInitSynIn, + ) -> Result { + const S: &str = "UsrPwd extension - Recv InitSyn."; + + let (_, ext_usrpwd) = input; + if ext_usrpwd.is_none() { + bail!("{S} Expected extension."); + } + + Ok(()) + } + + type SendInitAckIn = &'a StateAccept; + type SendInitAckOut = Option; + async fn send_init_ack( + &self, + state: Self::SendInitAckIn, + ) -> Result { + Ok(Some(ZExtZ64::new(state.nonce))) + } + + type RecvOpenSynIn = (&'a mut StateAccept, Option); + type RecvOpenSynOut = (); + async fn recv_open_syn( + &self, + input: Self::RecvOpenSynIn, + ) -> Result { + const S: &str = "UsrPwd extension - Recv OpenSyn."; + + let (state, mut ext_usrpwd) = input; + let ext_usrpwd = ext_usrpwd + .take() + .ok_or_else(|| zerror!("{S} Expected extension."))?; + + let codec = Zenoh080::new(); + let mut reader = ext_usrpwd.value.reader(); + let open_syn: OpenSyn = codec + .read(&mut reader) + .map_err(|_| zerror!("{S} Decoding error."))?; + + let r_inner = zasyncread!(self.inner); + let pwd = r_inner + .lookup + .get(&open_syn.user) + .ok_or_else(|| zerror!("{S} Invalid user."))?; + + // Create the HMAC of the password using the nonce received as challenge + let key = state.nonce.to_le_bytes(); + let hmac = hmac::sign(&key, pwd).map_err(|_| zerror!("{S} Encoding error."))?; + if hmac != open_syn.hmac { + bail!("{S} Invalid password."); + } + + Ok(()) + } + + type SendOpenAckIn = &'a StateAccept; + type SendOpenAckOut = Option; + async fn send_open_ack( + &self, + _input: Self::SendOpenAckIn, + ) -> Result { + Ok(Some(ZExtUnit::new())) + } +} diff --git a/io/zenoh-transport/src/unicast/establishment/ext/mod.rs b/io/zenoh-transport/src/unicast/establishment/ext/mod.rs new file mode 100644 index 0000000000..30e7a12b53 --- /dev/null +++ b/io/zenoh-transport/src/unicast/establishment/ext/mod.rs @@ -0,0 +1,20 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +#[cfg(feature = "transport_auth")] +pub mod auth; +#[cfg(feature = "transport_multilink")] +pub(crate) mod multilink; +pub(crate) mod qos; +#[cfg(feature = "shared-memory")] +pub(crate) mod shm; diff --git a/io/zenoh-transport/src/unicast/establishment/ext/multilink.rs b/io/zenoh-transport/src/unicast/establishment/ext/multilink.rs new file mode 100644 index 0000000000..7a3f0d9f30 --- /dev/null +++ b/io/zenoh-transport/src/unicast/establishment/ext/multilink.rs @@ -0,0 +1,363 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::unicast::establishment::{ + ext::auth::pubkey::{self, AuthPubKey, AuthPubKeyFsm, ZPublicKey}, + AcceptFsm, OpenFsm, +}; +use async_std::sync::{Mutex, RwLock}; +use async_trait::async_trait; +use rand::{CryptoRng, Rng}; +use rsa::{BigUint, RsaPrivateKey, RsaPublicKey}; +use zenoh_buffers::{ + reader::{DidntRead, HasReader, Reader}, + writer::{DidntWrite, Writer}, +}; +use zenoh_codec::{RCodec, WCodec, Zenoh080}; +use zenoh_core::{zerror, Error as ZError, Result as ZResult}; +use zenoh_crypto::PseudoRng; +use zenoh_protocol::transport::{init, open}; + +const KEY_SIZE: usize = 512; + +// Extension Fsm +pub(crate) struct MultiLink { + pubkey: RwLock, +} + +impl MultiLink { + pub(crate) fn make(rng: &mut R) -> ZResult + where + R: Rng + CryptoRng, + { + let pri_key = RsaPrivateKey::new(rng, KEY_SIZE)?; + let pub_key = RsaPublicKey::from(&pri_key); + let mut auth = AuthPubKey::new(pub_key.into(), pri_key.into()); + auth.disable_lookup(); + Ok(Self { + pubkey: RwLock::new(auth), + }) + } + + pub(crate) fn open(&self, is_multilink: bool) -> StateOpen { + StateOpen { + pubkey: is_multilink.then_some(( + pubkey::StateOpen::new(), + RsaPublicKey::new_unchecked(BigUint::new(vec![]), BigUint::new(vec![])).into(), + )), + } + } + + pub(crate) fn accept(&self, is_multilink: bool) -> StateAccept { + StateAccept { + pubkey: is_multilink.then_some(( + pubkey::StateAccept::new(), + RsaPublicKey::new_unchecked(BigUint::new(vec![]), BigUint::new(vec![])).into(), + )), + } + } + + pub(crate) fn fsm<'a>(&'a self, prng: &'a Mutex) -> MultiLinkFsm<'a> { + MultiLinkFsm { + fsm: AuthPubKeyFsm::new(&self.pubkey, prng), + } + } +} + +pub(crate) struct MultiLinkFsm<'a> { + fsm: AuthPubKeyFsm<'a>, +} + +/*************************************/ +/* OPEN */ +/*************************************/ +pub(crate) struct StateOpen { + pubkey: Option<(pubkey::StateOpen, ZPublicKey)>, +} + +impl StateOpen { + pub(crate) fn multilink(&self) -> Option { + self.pubkey.as_ref().map(|(_, p)| p.clone()) + } +} + +#[async_trait] +impl<'a> OpenFsm for MultiLinkFsm<'a> { + type Error = ZError; + + type SendInitSynIn = &'a StateOpen; + type SendInitSynOut = Option; + async fn send_init_syn( + &self, + input: Self::SendInitSynIn, + ) -> Result { + let pubkey = match input.pubkey.as_ref() { + Some(pubkey) => pubkey, + None => return Ok(None), + }; + + let r = self + .fsm + .send_init_syn(&pubkey.0) + .await? + .map(|x| x.transmute()); + Ok(r) + } + + type RecvInitAckIn = (&'a mut StateOpen, Option); + type RecvInitAckOut = (); + async fn recv_init_ack( + &self, + input: Self::RecvInitAckIn, + ) -> Result { + const S: &str = "MultiLink extension - Recv InitAck."; + + let (state, mut ext) = input; + let mut pubkey = match state.pubkey.take() { + Some(pubkey) => pubkey, + None => return Ok(()), + }; + + match ext.take() { + Some(ext) => { + let codec = Zenoh080::new(); + let mut reader = ext.value.reader(); + let init_ack: pubkey::InitAck = codec + .read(&mut reader) + .map_err(|_| zerror!("{S} Decoding error."))?; + + self.fsm + .recv_init_ack((&mut pubkey.0, Some(ext.transmute()))) + .await?; + + state.pubkey = Some((pubkey.0, init_ack.bob_pubkey)); + } + None => { + state.pubkey = None; + } + } + Ok(()) + } + + type SendOpenSynIn = &'a StateOpen; + type SendOpenSynOut = Option; + async fn send_open_syn( + &self, + input: Self::SendOpenSynIn, + ) -> Result { + let pubkey = match input.pubkey.as_ref() { + Some(pubkey) => pubkey, + None => return Ok(None), + }; + + let r = self + .fsm + .send_open_syn(&pubkey.0) + .await? + .map(|x| x.transmute()); + Ok(r) + } + + type RecvOpenAckIn = (&'a mut StateOpen, Option); + type RecvOpenAckOut = (); + async fn recv_open_ack( + &self, + input: Self::RecvOpenAckIn, + ) -> Result { + let (state, mut ext) = input; + let pubkey = match state.pubkey.as_mut() { + Some(pubkey) => pubkey, + None => return Ok(()), + }; + + match ext.take() { + Some(ext) => { + self.fsm + .recv_open_ack((&mut pubkey.0, Some(ext.transmute()))) + .await?; + } + None => { + state.pubkey = None; + } + } + + Ok(()) + } +} + +/*************************************/ +/* ACCEPT */ +/*************************************/ +#[derive(Debug, PartialEq)] +pub(crate) struct StateAccept { + pubkey: Option<(pubkey::StateAccept, ZPublicKey)>, +} + +impl StateAccept { + pub(crate) fn multilink(&self) -> Option { + self.pubkey.as_ref().map(|(_, p)| p.clone()) + } + + #[cfg(test)] + pub(crate) fn rand() -> Self { + let mut rng = rand::thread_rng(); + let pubkey = if rng.gen_bool(0.5) { + let n = BigUint::from_bytes_le(&[ + 0x41, 0x74, 0xc6, 0x40, 0x18, 0x63, 0xbd, 0x59, 0xe6, 0x0d, 0xe9, 0x23, 0x3e, 0x95, + 0xca, 0xb4, 0x5d, 0x17, 0x3d, 0x14, 0xdd, 0xbb, 0x16, 0x4a, 0x49, 0xeb, 0x43, 0x27, + 0x79, 0x3e, 0x75, 0x67, 0xd6, 0xf6, 0x7f, 0xe7, 0xbf, 0xb5, 0x1d, 0xf6, 0x27, 0x80, + 0xca, 0x26, 0x35, 0xa2, 0xc5, 0x4c, 0x96, 0x50, 0xaa, 0x9f, 0xf4, 0x47, 0xbe, 0x06, + 0x9c, 0xd1, 0xec, 0xfd, 0x1e, 0x81, 0xe9, 0xc4, + ]); + let e = BigUint::from_bytes_le(&[0x01, 0x00, 0x01]); + let pub_key = RsaPublicKey::new(n, e).unwrap(); + Some((pubkey::StateAccept::rand(), pub_key.into())) + } else { + None + }; + Self { pubkey } + } +} + +impl WCodec<&StateAccept, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &StateAccept) -> Self::Output { + match x.pubkey.as_ref() { + Some((s, p)) => { + self.write(&mut *writer, 1u8)?; + self.write(&mut *writer, s)?; + self.write(&mut *writer, p) + } + None => self.write(&mut *writer, 0u8), + } + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let is_multilink: u8 = self.read(&mut *reader)?; + let pubkey = if is_multilink == 1 { + let s: pubkey::StateAccept = self.read(&mut *reader)?; + let p: ZPublicKey = self.read(&mut *reader)?; + Some((s, p)) + } else { + None + }; + Ok(StateAccept { pubkey }) + } +} + +#[async_trait] +impl<'a> AcceptFsm for MultiLinkFsm<'a> { + type Error = ZError; + + type RecvInitSynIn = (&'a mut StateAccept, Option); + type RecvInitSynOut = (); + async fn recv_init_syn( + &self, + input: Self::RecvInitSynIn, + ) -> Result { + const S: &str = "MultiLink extension - Recv InitSyn."; + + let (state, mut ext) = input; + let mut pubkey = match state.pubkey.take() { + Some(pubkey) => pubkey, + None => return Ok(()), + }; + + match ext.take() { + Some(ext) => { + let codec = Zenoh080::new(); + let mut reader = ext.value.reader(); + let init_syn: pubkey::InitSyn = codec + .read(&mut reader) + .map_err(|_| zerror!("{S} Decoding error."))?; + + self.fsm + .recv_init_syn((&mut pubkey.0, Some(ext.transmute()))) + .await?; + + state.pubkey = Some((pubkey.0, init_syn.alice_pubkey)); + } + None => { + state.pubkey = None; + } + } + + Ok(()) + } + + type SendInitAckIn = &'a StateAccept; + type SendInitAckOut = Option; + async fn send_init_ack( + &self, + input: Self::SendInitAckIn, + ) -> Result { + let pubkey = match input.pubkey.as_ref() { + Some(pubkey) => pubkey, + None => return Ok(None), + }; + + let r = self + .fsm + .send_init_ack(&pubkey.0) + .await? + .map(|x| x.transmute()); + Ok(r) + } + + type RecvOpenSynIn = (&'a mut StateAccept, Option); + type RecvOpenSynOut = (); + async fn recv_open_syn( + &self, + input: Self::RecvOpenSynIn, + ) -> Result { + let (state, ext) = input; + let pubkey = match state.pubkey.as_mut() { + Some(pubkey) => pubkey, + None => return Ok(()), + }; + + self.fsm + .recv_open_syn((&mut pubkey.0, ext.map(|x| x.transmute()))) + .await + } + + type SendOpenAckIn = &'a StateAccept; + type SendOpenAckOut = Option; + async fn send_open_ack( + &self, + input: Self::SendOpenAckIn, + ) -> Result { + let pubkey = match input.pubkey.as_ref() { + Some(pubkey) => pubkey, + None => return Ok(None), + }; + + let r = self + .fsm + .send_open_ack(&pubkey.0) + .await? + .map(|x| x.transmute()); + Ok(r) + } +} diff --git a/io/zenoh-transport/src/unicast/establishment/ext/qos.rs b/io/zenoh-transport/src/unicast/establishment/ext/qos.rs new file mode 100644 index 0000000000..b72e34c636 --- /dev/null +++ b/io/zenoh-transport/src/unicast/establishment/ext/qos.rs @@ -0,0 +1,192 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::unicast::establishment::{AcceptFsm, OpenFsm}; +use async_trait::async_trait; +use core::marker::PhantomData; +use zenoh_buffers::{ + reader::{DidntRead, Reader}, + writer::{DidntWrite, Writer}, +}; +use zenoh_codec::{RCodec, WCodec, Zenoh080}; +use zenoh_protocol::transport::{init, open}; +use zenoh_result::Error as ZError; + +// Extension Fsm +pub(crate) struct QoSFsm<'a> { + _a: PhantomData<&'a ()>, +} + +impl<'a> QoSFsm<'a> { + pub(crate) const fn new() -> Self { + Self { _a: PhantomData } + } +} + +/*************************************/ +/* OPEN */ +/*************************************/ +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) struct StateOpen { + is_qos: bool, +} + +impl StateOpen { + pub(crate) const fn new(is_qos: bool) -> Self { + Self { is_qos } + } + + pub(crate) const fn is_qos(&self) -> bool { + self.is_qos + } +} + +#[async_trait] +impl<'a> OpenFsm for QoSFsm<'a> { + type Error = ZError; + + type SendInitSynIn = &'a StateOpen; + type SendInitSynOut = Option; + async fn send_init_syn( + &self, + state: Self::SendInitSynIn, + ) -> Result { + let output = state.is_qos.then_some(init::ext::QoS::new()); + Ok(output) + } + + type RecvInitAckIn = (&'a mut StateOpen, Option); + type RecvInitAckOut = (); + async fn recv_init_ack( + &self, + input: Self::RecvInitAckIn, + ) -> Result { + let (state, other_ext) = input; + state.is_qos &= other_ext.is_some(); + Ok(()) + } + + type SendOpenSynIn = &'a StateOpen; + type SendOpenSynOut = Option; + async fn send_open_syn( + &self, + _state: Self::SendOpenSynIn, + ) -> Result { + Ok(None) + } + + type RecvOpenAckIn = (&'a mut StateOpen, Option); + type RecvOpenAckOut = (); + async fn recv_open_ack( + &self, + _state: Self::RecvOpenAckIn, + ) -> Result { + Ok(()) + } +} + +/*************************************/ +/* ACCEPT */ +/*************************************/ +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) struct StateAccept { + is_qos: bool, +} + +impl StateAccept { + pub(crate) const fn new(is_qos: bool) -> Self { + Self { is_qos } + } + + pub(crate) const fn is_qos(&self) -> bool { + self.is_qos + } + + #[cfg(test)] + pub(crate) fn rand() -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); + Self::new(rng.gen_bool(0.5)) + } +} + +// Codec +impl WCodec<&StateAccept, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &StateAccept) -> Self::Output { + let is_qos = u8::from(x.is_qos); + self.write(&mut *writer, is_qos)?; + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let is_qos: u8 = self.read(&mut *reader)?; + let is_qos = is_qos == 1; + Ok(StateAccept { is_qos }) + } +} + +#[async_trait] +impl<'a> AcceptFsm for QoSFsm<'a> { + type Error = ZError; + + type RecvInitSynIn = (&'a mut StateAccept, Option); + type RecvInitSynOut = (); + async fn recv_init_syn( + &self, + input: Self::RecvInitSynIn, + ) -> Result { + let (state, other_ext) = input; + state.is_qos &= other_ext.is_some(); + Ok(()) + } + + type SendInitAckIn = &'a StateAccept; + type SendInitAckOut = Option; + async fn send_init_ack( + &self, + state: Self::SendInitAckIn, + ) -> Result { + let output = state.is_qos.then_some(init::ext::QoS::new()); + Ok(output) + } + + type RecvOpenSynIn = (&'a mut StateAccept, Option); + type RecvOpenSynOut = (); + async fn recv_open_syn( + &self, + _state: Self::RecvOpenSynIn, + ) -> Result { + Ok(()) + } + + type SendOpenAckIn = &'a StateAccept; + type SendOpenAckOut = Option; + async fn send_open_ack( + &self, + _state: Self::SendOpenAckIn, + ) -> Result { + Ok(None) + } +} diff --git a/io/zenoh-transport/src/unicast/establishment/ext/shm.rs b/io/zenoh-transport/src/unicast/establishment/ext/shm.rs new file mode 100644 index 0000000000..131c0b5186 --- /dev/null +++ b/io/zenoh-transport/src/unicast/establishment/ext/shm.rs @@ -0,0 +1,495 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::unicast::{ + establishment::{AcceptFsm, OpenFsm}, + shared_memory_unicast::{Challenge, SharedMemoryUnicast}, +}; +use async_trait::async_trait; +use std::convert::TryInto; +use zenoh_buffers::{ + reader::{DidntRead, HasReader, Reader}, + writer::{DidntWrite, HasWriter, Writer}, +}; +use zenoh_codec::{RCodec, WCodec, Zenoh080}; +use zenoh_core::zasyncwrite; +use zenoh_protocol::transport::{init, open}; +use zenoh_result::{zerror, Error as ZError}; +use zenoh_shm::SharedMemoryBufInfo; + +/*************************************/ +/* InitSyn */ +/*************************************/ +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// ~ ShmMemBufInfo ~ +/// +---------------+ +pub(crate) struct InitSyn { + pub(crate) alice_info: SharedMemoryBufInfo, +} + +// Codec +impl WCodec<&InitSyn, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &InitSyn) -> Self::Output { + self.write(&mut *writer, &x.alice_info)?; + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let alice_info: SharedMemoryBufInfo = self.read(&mut *reader)?; + Ok(InitSyn { alice_info }) + } +} + +/*************************************/ +/* InitAck */ +/*************************************/ +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// ~ challenge ~ +/// +---------------+ +/// ~ ShmMemBufInfo ~ +/// +---------------+ +struct InitAck { + alice_challenge: u64, + bob_info: SharedMemoryBufInfo, +} + +impl WCodec<&InitAck, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &InitAck) -> Self::Output { + self.write(&mut *writer, x.alice_challenge)?; + self.write(&mut *writer, &x.bob_info)?; + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let alice_challenge: u64 = self.read(&mut *reader)?; + let bob_info: SharedMemoryBufInfo = self.read(&mut *reader)?; + Ok(InitAck { + alice_challenge, + bob_info, + }) + } +} + +/*************************************/ +/* OpenSyn */ +/*************************************/ +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// ~ challenge ~ +/// +---------------+ + +/*************************************/ +/* OpenAck */ +/*************************************/ +/// 7 6 5 4 3 2 1 0 +/// +-+-+-+-+-+-+-+-+ +/// ~ ack ~ +/// +---------------+ + +// Extension Fsm +pub(crate) struct ShmFsm<'a> { + inner: &'a SharedMemoryUnicast, +} + +impl<'a> ShmFsm<'a> { + pub(crate) const fn new(inner: &'a SharedMemoryUnicast) -> Self { + Self { inner } + } +} + +/*************************************/ +/* OPEN */ +/*************************************/ +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) struct StateOpen { + is_shm: bool, +} + +impl StateOpen { + pub(crate) const fn new(is_shm: bool) -> Self { + Self { is_shm } + } + + pub(crate) const fn is_shm(&self) -> bool { + self.is_shm + } +} + +#[async_trait] +impl<'a> OpenFsm for ShmFsm<'a> { + type Error = ZError; + + type SendInitSynIn = &'a StateOpen; + type SendInitSynOut = Option; + async fn send_init_syn( + &self, + state: Self::SendInitSynIn, + ) -> Result { + const S: &str = "Shm extension - Send InitSyn."; + + if !state.is_shm() { + return Ok(None); + } + + let init_syn = InitSyn { + alice_info: self.inner.challenge.info.clone(), + }; + + let codec = Zenoh080::new(); + let mut buff = vec![]; + let mut writer = buff.writer(); + codec + .write(&mut writer, &init_syn) + .map_err(|_| zerror!("{} Encoding error", S))?; + + Ok(Some(init::ext::Shm::new(buff.into()))) + } + + type RecvInitAckIn = (&'a mut StateOpen, Option); + type RecvInitAckOut = Challenge; + async fn recv_init_ack( + &self, + input: Self::RecvInitAckIn, + ) -> Result { + const S: &str = "Shm extension - Recv InitAck."; + + let (state, mut ext) = input; + if !state.is_shm() { + return Ok(0); + } + + let Some(ext) = ext.take() else { + state.is_shm = false; + return Ok(0); + }; + + // Decode the extension + let codec = Zenoh080::new(); + let mut reader = ext.value.reader(); + let Ok(init_ack): Result = codec.read(&mut reader) else { + log::trace!("{} Decoding error.", S); + state.is_shm = false; + return Ok(0); + }; + + // Alice challenge as seen by Alice + let bytes: [u8; std::mem::size_of::()] = self + .inner + .challenge + .as_slice() + .try_into() + .map_err(|e| zerror!("{}", e))?; + let challenge = u64::from_le_bytes(bytes); + + // Verify that Bob has correctly read Alice challenge + if challenge != init_ack.alice_challenge { + log::trace!( + "{} Challenge mismatch: {} != {}.", + S, + init_ack.alice_challenge, + challenge + ); + state.is_shm = false; + return Ok(0); + } + + // Read Bob's SharedMemoryBuf + let shm_buff = match zasyncwrite!(self.inner.reader).read_shmbuf(&init_ack.bob_info) { + Ok(buff) => buff, + Err(e) => { + log::trace!("{} {}", S, e); + state.is_shm = false; + return Ok(0); + } + }; + + // Bob challenge as seen by Alice + let bytes: [u8; std::mem::size_of::()] = match shm_buff.as_slice().try_into() { + Ok(bytes) => bytes, + Err(_) => { + log::trace!("{} Failed to read remote Shm.", S); + state.is_shm = false; + return Ok(0); + } + }; + let bob_challenge = u64::from_le_bytes(bytes); + + Ok(bob_challenge) + } + + type SendOpenSynIn = (&'a StateOpen, Self::RecvInitAckOut); + type SendOpenSynOut = Option; + async fn send_open_syn( + &self, + input: Self::SendOpenSynIn, + ) -> Result { + // const S: &str = "Shm extension - Send OpenSyn."; + + let (state, bob_challenge) = input; + if !state.is_shm() { + return Ok(None); + } + + Ok(Some(open::ext::Shm::new(bob_challenge))) + } + + type RecvOpenAckIn = (&'a mut StateOpen, Option); + type RecvOpenAckOut = (); + async fn recv_open_ack( + &self, + input: Self::RecvOpenAckIn, + ) -> Result { + const S: &str = "Shm extension - Recv OpenAck."; + + let (state, mut ext) = input; + if !state.is_shm() { + return Ok(()); + } + + let Some(ext) = ext.take() else { + state.is_shm = false; + return Ok(()); + }; + + if ext.value != 1 { + log::trace!("{} Invalid value.", S); + state.is_shm = false; + return Ok(()); + } + + state.is_shm = true; + Ok(()) + } +} + +/*************************************/ +/* ACCEPT */ +/*************************************/ + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) struct StateAccept { + is_shm: bool, +} + +impl StateAccept { + pub(crate) const fn new(is_shm: bool) -> Self { + Self { is_shm } + } + + pub(crate) const fn is_shm(&self) -> bool { + self.is_shm + } + + #[cfg(test)] + pub(crate) fn rand() -> Self { + use rand::Rng; + let mut rng = rand::thread_rng(); + Self::new(rng.gen_bool(0.5)) + } +} + +// Codec +impl WCodec<&StateAccept, &mut W> for Zenoh080 +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &StateAccept) -> Self::Output { + let is_shm = u8::from(x.is_shm); + self.write(&mut *writer, is_shm)?; + Ok(()) + } +} + +impl RCodec for Zenoh080 +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let is_shm: u8 = self.read(&mut *reader)?; + let is_shm = is_shm == 1; + Ok(StateAccept { is_shm }) + } +} + +#[async_trait] +impl<'a> AcceptFsm for ShmFsm<'a> { + type Error = ZError; + + type RecvInitSynIn = (&'a mut StateAccept, Option); + type RecvInitSynOut = Challenge; + async fn recv_init_syn( + &self, + input: Self::RecvInitSynIn, + ) -> Result { + const S: &str = "Shm extension - Recv InitSyn."; + + let (state, mut ext) = input; + if !state.is_shm() { + return Ok(0); + } + + let Some(ext) = ext.take() else { + state.is_shm = false; + return Ok(0); + }; + + // Decode the extension + let codec = Zenoh080::new(); + let mut reader = ext.value.reader(); + let Ok(init_syn): Result = codec.read(&mut reader) else { + log::trace!("{} Decoding error.", S); + state.is_shm = false; + return Ok(0); + }; + + // Read Alice's SharedMemoryBuf + let shm_buff = match zasyncwrite!(self.inner.reader).read_shmbuf(&init_syn.alice_info) { + Ok(buff) => buff, + Err(e) => { + log::trace!("{} {}", S, e); + state.is_shm = false; + return Ok(0); + } + }; + + // Alice challenge as seen by Bob + let bytes: [u8; std::mem::size_of::()] = match shm_buff.as_slice().try_into() { + Ok(bytes) => bytes, + Err(_) => { + log::trace!("{} Failed to read remote Shm.", S); + state.is_shm = false; + return Ok(0); + } + }; + let alice_challenge = u64::from_le_bytes(bytes); + + Ok(alice_challenge) + } + + type SendInitAckIn = (&'a StateAccept, Self::RecvInitSynOut); + type SendInitAckOut = Option; + async fn send_init_ack( + &self, + input: Self::SendInitAckIn, + ) -> Result { + const S: &str = "Shm extension - Send InitAck."; + + let (state, alice_challenge) = input; + if !state.is_shm() { + return Ok(None); + } + + let init_syn = InitAck { + alice_challenge, + bob_info: self.inner.challenge.info.clone(), + }; + + let codec = Zenoh080::new(); + let mut buff = vec![]; + let mut writer = buff.writer(); + codec + .write(&mut writer, &init_syn) + .map_err(|_| zerror!("{} Encoding error", S))?; + + Ok(Some(init::ext::Shm::new(buff.into()))) + } + + type RecvOpenSynIn = (&'a mut StateAccept, Option); + type RecvOpenSynOut = (); + async fn recv_open_syn( + &self, + input: Self::RecvOpenSynIn, + ) -> Result { + const S: &str = "Shm extension - Recv OpenSyn."; + + let (state, mut ext) = input; + if !state.is_shm() { + return Ok(()); + } + + let Some(ext) = ext.take() else { + state.is_shm = false; + return Ok(()); + }; + + // Bob challenge as seen by Bob + let bytes: [u8; std::mem::size_of::()] = self + .inner + .challenge + .as_slice() + .try_into() + .map_err(|e| zerror!("{}", e))?; + let challenge = u64::from_le_bytes(bytes); + + // Verify that Alice has correctly read Bob challenge + let bob_challnge = ext.value; + if challenge != bob_challnge { + log::trace!( + "{} Challenge mismatch: {} != {}.", + S, + bob_challnge, + challenge + ); + state.is_shm = false; + return Ok(()); + } + + Ok(()) + } + + type SendOpenAckIn = &'a mut StateAccept; + type SendOpenAckOut = Option; + async fn send_open_ack( + &self, + state: Self::SendOpenAckIn, + ) -> Result { + // const S: &str = "Shm extension - Send OpenAck."; + + if !state.is_shm() { + return Ok(None); + } + + state.is_shm = true; + Ok(Some(open::ext::Shm::new(1))) + } +} diff --git a/io/zenoh-transport/src/unicast/establishment/mod.rs b/io/zenoh-transport/src/unicast/establishment/mod.rs index 75c3e78c24..6bc8c898e8 100644 --- a/io/zenoh-transport/src/unicast/establishment/mod.rs +++ b/io/zenoh-transport/src/unicast/establishment/mod.rs @@ -12,85 +12,133 @@ // ZettaScale Zenoh Team, // pub(crate) mod accept; -pub mod authenticator; pub(super) mod cookie; +pub mod ext; pub(crate) mod open; -pub(super) mod properties; -use super::super::TransportManager; -use super::{TransportConfigUnicast, TransportPeer, TransportUnicast}; -use authenticator::AuthenticatedPeerLink; +use super::{TransportPeer, TransportUnicast}; +use crate::{common::seq_num, TransportManager}; +use async_trait::async_trait; use cookie::*; -use properties::*; -use rand::Rng; +use sha3::{ + digest::{ExtendableOutput, Update, XofReader}, + Shake128, +}; use std::time::Duration; -use zenoh_core::{zasynclock, zasyncread}; use zenoh_link::{Link, LinkUnicast}; use zenoh_protocol::{ - core::{WhatAmI, ZInt, ZenohId}, - transport::TransportMessage, + core::{Field, Resolution, ZenohId}, + transport::{BatchSize, Close, TransportMessage, TransportSn}, }; use zenoh_result::ZResult; -pub(super) async fn close_link( - link: &LinkUnicast, - manager: &TransportManager, - auth_link: &AuthenticatedPeerLink, - mut reason: Option, -) { - if let Some(reason) = reason.take() { - // Build the close message - let peer_id = Some(manager.config.zid); - let link_only = true; - let attachment = None; - let message = TransportMessage::make_close(peer_id, reason, link_only, attachment); - // Send the close message on the link - let _ = link.write_transport_message(&message).await; - } +/*************************************/ +/* TRAITS */ +/*************************************/ +#[async_trait] +pub trait OpenFsm { + type Error; - // Close the link - let _ = link.close().await; - // Notify the authenticators - for pa in zasyncread!(manager.state.unicast.peer_authenticator).iter() { - pa.handle_link_err(auth_link).await; - } + type SendInitSynIn; + type SendInitSynOut; + async fn send_init_syn( + &self, + input: Self::SendInitSynIn, + ) -> Result; + + type RecvInitAckIn; + type RecvInitAckOut; + async fn recv_init_ack( + &self, + input: Self::RecvInitAckIn, + ) -> Result; + + type SendOpenSynIn; + type SendOpenSynOut; + async fn send_open_syn( + &self, + input: Self::SendOpenSynIn, + ) -> Result; + + type RecvOpenAckIn; + type RecvOpenAckOut; + async fn recv_open_ack( + &self, + input: Self::RecvOpenAckIn, + ) -> Result; +} + +#[async_trait] +pub trait AcceptFsm { + type Error; + + type RecvInitSynIn; + type RecvInitSynOut; + async fn recv_init_syn( + &self, + input: Self::RecvInitSynIn, + ) -> Result; + + type SendInitAckIn; + type SendInitAckOut; + async fn send_init_ack( + &self, + input: Self::SendInitAckIn, + ) -> Result; + + type RecvOpenSynIn; + type RecvOpenSynOut; + async fn recv_open_syn( + &self, + input: Self::RecvOpenSynIn, + ) -> Result; + + type SendOpenAckIn; + type SendOpenAckOut; + async fn send_open_ack( + &self, + input: Self::SendOpenAckIn, + ) -> Result; } /*************************************/ -/* TRANSPORT */ +/* FUNCTIONS */ /*************************************/ -pub(super) struct InputInit { - pub(super) zid: ZenohId, - pub(super) whatami: WhatAmI, - pub(super) sn_resolution: ZInt, - pub(super) is_shm: bool, - pub(super) is_qos: bool, +pub(super) fn compute_sn(zid1: ZenohId, zid2: ZenohId, resolution: Resolution) -> TransportSn { + // Create a random yet deterministic initial_sn. + // In case of multilink it's important that the same initial_sn is used for every connection attempt. + // Instead of storing the state everywhere, we make sure that the we always compute the same initial_sn. + let mut hasher = Shake128::default(); + hasher.update(&zid1.to_le_bytes()[..zid1.size()]); + hasher.update(&zid2.to_le_bytes()[..zid2.size()]); + let mut array = (0 as TransportSn).to_le_bytes(); + hasher.finalize_xof().read(&mut array); + TransportSn::from_le_bytes(array) & seq_num::get_mask(resolution.get(Field::FrameSN)) } -async fn transport_init( - manager: &TransportManager, - input: self::InputInit, -) -> ZResult { - // Initialize the transport if it is new - let initial_sn_tx = zasynclock!(manager.prng).gen_range(0..input.sn_resolution); - - let config = TransportConfigUnicast { - peer: input.zid, - whatami: input.whatami, - sn_resolution: input.sn_resolution, - is_shm: input.is_shm, - is_qos: input.is_qos, - initial_sn_tx, - }; - - manager.init_transport_unicast(config) + +pub(super) async fn close_link(link: &LinkUnicast, reason: Option) { + if let Some(reason) = reason { + // Build the close message + let message: TransportMessage = Close { + reason, + session: false, + } + .into(); + // Send the close message on the link + let _ = link.send(&message).await; + } + + // Close the link + let _ = link.close().await; } pub(super) struct InputFinalize { pub(super) transport: TransportUnicast, - pub(super) lease: Duration, + pub(super) other_lease: Duration, + pub(super) agreed_batch_size: BatchSize, } // Finalize the transport, notify the callback and start the link tasks -pub(super) async fn transport_finalize( +pub(super) async fn finalize_transport( link: &LinkUnicast, manager: &TransportManager, input: self::InputFinalize, @@ -104,7 +152,7 @@ pub(super) async fn transport_finalize( link, &manager.tx_executor, keep_alive, - manager.config.batch_size, + input.agreed_batch_size, )?; // Assign a callback if the transport is new @@ -114,9 +162,10 @@ pub(super) async fn transport_finalize( let peer = TransportPeer { zid: transport.get_zid(), whatami: transport.get_whatami(), + links: vec![Link::from(link)], is_qos: transport.is_qos(), + #[cfg(feature = "shared-memory")] is_shm: transport.is_shm(), - links: vec![Link::from(link)], }; // Notify the transport handler that there is a new transport and get back a callback // NOTE: the read loop of the link the open message was sent on remains blocked @@ -136,7 +185,7 @@ pub(super) async fn transport_finalize( drop(a_guard); // Start the RX loop - transport.start_rx(link, input.lease)?; + transport.start_rx(link, input.other_lease, input.agreed_batch_size)?; Ok(()) } diff --git a/io/zenoh-transport/src/unicast/establishment/open.rs b/io/zenoh-transport/src/unicast/establishment/open.rs new file mode 100644 index 0000000000..e7d78c6537 --- /dev/null +++ b/io/zenoh-transport/src/unicast/establishment/open.rs @@ -0,0 +1,550 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +#[cfg(feature = "shared-memory")] +use crate::unicast::shared_memory_unicast::Challenge; +use crate::{ + unicast::establishment::{ + close_link, compute_sn, ext, finalize_transport, InputFinalize, OpenFsm, + }, + TransportConfigUnicast, TransportManager, TransportUnicast, +}; +use async_trait::async_trait; +use std::time::Duration; +use zenoh_buffers::ZSlice; +#[cfg(feature = "transport_auth")] +use zenoh_core::zasynclock; +use zenoh_core::{zcondfeat, zerror}; +use zenoh_link::{LinkUnicast, LinkUnicastDirection}; +use zenoh_protocol::{ + core::{Field, Resolution, WhatAmI, ZenohId}, + transport::{ + batch_size, close, BatchSize, Close, InitSyn, OpenSyn, TransportBody, TransportMessage, + TransportSn, + }, +}; +use zenoh_result::ZResult; + +type OpenError = (zenoh_result::Error, Option); + +struct StateZenoh { + batch_size: BatchSize, + resolution: Resolution, +} + +struct State { + zenoh: StateZenoh, + ext_qos: ext::qos::StateOpen, + #[cfg(feature = "transport_multilink")] + ext_mlink: ext::multilink::StateOpen, + #[cfg(feature = "shared-memory")] + ext_shm: ext::shm::StateOpen, + #[cfg(feature = "transport_auth")] + ext_auth: ext::auth::StateOpen, +} + +// InitSyn +struct SendInitSynIn { + mine_version: u8, + mine_zid: ZenohId, + mine_whatami: WhatAmI, +} + +// InitAck +struct RecvInitAckOut { + other_zid: ZenohId, + other_whatami: WhatAmI, + other_cookie: ZSlice, + #[cfg(feature = "shared-memory")] + ext_shm: Challenge, +} + +// OpenSyn +struct SendOpenSynIn { + mine_zid: ZenohId, + mine_lease: Duration, + other_zid: ZenohId, + other_cookie: ZSlice, + #[cfg(feature = "shared-memory")] + ext_shm: Challenge, +} + +struct SendOpenSynOut { + mine_initial_sn: TransportSn, +} + +// OpenAck +struct RecvOpenAckOut { + other_lease: Duration, + other_initial_sn: TransportSn, +} + +// FSM +struct OpenLink<'a> { + link: &'a LinkUnicast, + ext_qos: ext::qos::QoSFsm<'a>, + #[cfg(feature = "transport_multilink")] + ext_mlink: ext::multilink::MultiLinkFsm<'a>, + #[cfg(feature = "shared-memory")] + ext_shm: ext::shm::ShmFsm<'a>, + #[cfg(feature = "transport_auth")] + ext_auth: ext::auth::AuthFsm<'a>, +} + +#[async_trait] +impl<'a> OpenFsm for OpenLink<'a> { + type Error = OpenError; + + type SendInitSynIn = (&'a mut State, SendInitSynIn); + type SendInitSynOut = (); + async fn send_init_syn( + &self, + input: Self::SendInitSynIn, + ) -> Result { + let (state, input) = input; + + // Extension QoS + let ext_qos = self + .ext_qos + .send_init_syn(&state.ext_qos) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?; + + // Extension Shm + let ext_shm = zcondfeat!( + "shared-memory", + self.ext_shm + .send_init_syn(&state.ext_shm) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?, + None + ); + + // Extension Auth + let ext_auth = zcondfeat!( + "transport_auth", + self.ext_auth + .send_init_syn(&state.ext_auth) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?, + None + ); + + // Extension MultiLink + let ext_mlink = zcondfeat!( + "transport_multilink", + self.ext_mlink + .send_init_syn(&state.ext_mlink) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?, + None + ); + + let msg: TransportMessage = InitSyn { + version: input.mine_version, + whatami: input.mine_whatami, + zid: input.mine_zid, + batch_size: state.zenoh.batch_size, + resolution: state.zenoh.resolution, + ext_qos, + ext_shm, + ext_auth, + ext_mlink, + } + .into(); + + let _ = self + .link + .send(&msg) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?; + + Ok(()) + } + + type RecvInitAckIn = &'a mut State; + type RecvInitAckOut = RecvInitAckOut; + async fn recv_init_ack( + &self, + state: Self::RecvInitAckIn, + ) -> Result { + let msg = self + .link + .recv() + .await + .map_err(|e| (e, Some(close::reason::INVALID)))?; + + let init_ack = match msg.body { + TransportBody::InitAck(init_ack) => init_ack, + TransportBody::Close(Close { reason, .. }) => { + let e = zerror!( + "Received a close message (reason {}) in response to an InitSyn on: {}", + close::reason_to_str(reason), + self.link, + ); + match reason { + close::reason::MAX_LINKS => log::debug!("{}", e), + _ => log::error!("{}", e), + } + return Err((e.into(), None)); + } + _ => { + let e = zerror!( + "Received an invalid message in response to an InitSyn on {}: {:?}", + self.link, + msg.body + ); + log::error!("{}", e); + return Err((e.into(), Some(close::reason::INVALID))); + } + }; + + // Compute the minimum SN resolution + state.zenoh.resolution = { + let mut res = Resolution::default(); + + // Frame SN + let i_fsn_res = init_ack.resolution.get(Field::FrameSN); + let m_fsn_res = state.zenoh.resolution.get(Field::FrameSN); + + if i_fsn_res > m_fsn_res { + let e = zerror!( + "Invalid FrameSN resolution on {}: {:?} > {:?}", + self.link, + i_fsn_res, + m_fsn_res + ); + log::error!("{}", e); + return Err((e.into(), Some(close::reason::INVALID))); + } + res.set(Field::FrameSN, i_fsn_res); + + // Request ID + let i_rid_res = init_ack.resolution.get(Field::RequestID); + let m_rid_res = state.zenoh.resolution.get(Field::RequestID); + + if i_rid_res > m_rid_res { + let e = zerror!( + "Invalid RequestID resolution on {}: {:?} > {:?}", + self.link, + i_rid_res, + m_rid_res + ); + log::error!("{}", e); + return Err((e.into(), Some(close::reason::INVALID))); + } + res.set(Field::RequestID, i_rid_res); + + res + }; + + // Compute the minimum batch size + state.zenoh.batch_size = state.zenoh.batch_size.min(init_ack.batch_size); + + // Extension QoS + self.ext_qos + .recv_init_ack((&mut state.ext_qos, init_ack.ext_qos)) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?; + + // Extension Shm + #[cfg(feature = "shared-memory")] + let shm_challenge = self + .ext_shm + .recv_init_ack((&mut state.ext_shm, init_ack.ext_shm)) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?; + + // Extension Auth + #[cfg(feature = "transport_auth")] + self.ext_auth + .recv_init_ack((&mut state.ext_auth, init_ack.ext_auth)) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?; + + // Extension MultiLink + #[cfg(feature = "transport_multilink")] + self.ext_mlink + .recv_init_ack((&mut state.ext_mlink, init_ack.ext_mlink)) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?; + + let output = RecvInitAckOut { + other_zid: init_ack.zid, + other_whatami: init_ack.whatami, + other_cookie: init_ack.cookie, + #[cfg(feature = "shared-memory")] + ext_shm: shm_challenge, + }; + Ok(output) + } + + type SendOpenSynIn = (&'a mut State, SendOpenSynIn); + type SendOpenSynOut = SendOpenSynOut; + async fn send_open_syn( + &self, + input: Self::SendOpenSynIn, + ) -> Result { + let (state, input) = input; + + // Extension QoS + let ext_qos = self + .ext_qos + .send_open_syn(&state.ext_qos) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?; + + // Extension Shm + let ext_shm = zcondfeat!( + "shared-memory", + self.ext_shm + .send_open_syn((&state.ext_shm, input.ext_shm)) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?, + None + ); + + // Extension Auth + let ext_auth = zcondfeat!( + "transport_auth", + self.ext_auth + .send_open_syn(&state.ext_auth) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?, + None + ); + + // Extension MultiLink + let ext_mlink = zcondfeat!( + "transport_multilink", + self.ext_mlink + .send_open_syn(&state.ext_mlink) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?, + None + ); + + // Build and send an OpenSyn message + let mine_initial_sn = compute_sn(input.mine_zid, input.other_zid, state.zenoh.resolution); + let message: TransportMessage = OpenSyn { + lease: input.mine_lease, + initial_sn: mine_initial_sn, + cookie: input.other_cookie, + ext_qos, + ext_shm, + ext_auth, + ext_mlink, + } + .into(); + + let _ = self + .link + .send(&message) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?; + + let output = SendOpenSynOut { mine_initial_sn }; + Ok(output) + } + + type RecvOpenAckIn = &'a mut State; + type RecvOpenAckOut = RecvOpenAckOut; + async fn recv_open_ack( + &self, + state: Self::RecvOpenAckIn, + ) -> Result { + let msg = self + .link + .recv() + .await + .map_err(|e| (e, Some(close::reason::INVALID)))?; + + let open_ack = match msg.body { + TransportBody::OpenAck(open_ack) => open_ack, + TransportBody::Close(Close { reason, .. }) => { + let e = zerror!( + "Received a close message (reason {}) in response to an OpenSyn on: {:?}", + close::reason_to_str(reason), + self.link, + ); + match reason { + close::reason::MAX_LINKS => log::debug!("{}", e), + _ => log::error!("{}", e), + } + return Err((e.into(), None)); + } + _ => { + let e = zerror!( + "Received an invalid message in response to an OpenSyn on {}: {:?}", + self.link, + msg.body + ); + log::error!("{}", e); + return Err((e.into(), Some(close::reason::INVALID))); + } + }; + + // Extension QoS + self.ext_qos + .recv_open_ack((&mut state.ext_qos, open_ack.ext_qos)) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?; + + // Extension Shm + #[cfg(feature = "shared-memory")] + self.ext_shm + .recv_open_ack((&mut state.ext_shm, open_ack.ext_shm)) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?; + + // Extension Auth + #[cfg(feature = "transport_auth")] + self.ext_auth + .recv_open_ack((&mut state.ext_auth, open_ack.ext_auth)) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?; + + // Extension MultiLink + #[cfg(feature = "transport_multilink")] + self.ext_mlink + .recv_open_ack((&mut state.ext_mlink, open_ack.ext_mlink)) + .await + .map_err(|e| (e, Some(close::reason::GENERIC)))?; + + let output = RecvOpenAckOut { + other_initial_sn: open_ack.initial_sn, + other_lease: open_ack.lease, + }; + Ok(output) + } +} + +pub(crate) async fn open_link( + link: &LinkUnicast, + manager: &TransportManager, +) -> ZResult { + let fsm = OpenLink { + link, + ext_qos: ext::qos::QoSFsm::new(), + #[cfg(feature = "transport_multilink")] + ext_mlink: manager.state.unicast.multilink.fsm(&manager.prng), + #[cfg(feature = "shared-memory")] + ext_shm: ext::shm::ShmFsm::new(&manager.state.unicast.shm), + #[cfg(feature = "transport_auth")] + ext_auth: manager.state.unicast.authenticator.fsm(&manager.prng), + }; + + let mut state = State { + zenoh: StateZenoh { + batch_size: manager.config.batch_size.min(batch_size::UNICAST), + resolution: manager.config.resolution, + }, + ext_qos: ext::qos::StateOpen::new(manager.config.unicast.is_qos), + #[cfg(feature = "transport_multilink")] + ext_mlink: manager + .state + .unicast + .multilink + .open(manager.config.unicast.max_links > 1), + #[cfg(feature = "shared-memory")] + ext_shm: ext::shm::StateOpen::new(manager.config.unicast.is_shm), + #[cfg(feature = "transport_auth")] + ext_auth: manager + .state + .unicast + .authenticator + .open(&mut *zasynclock!(manager.prng)), + }; + + // Init handshake + macro_rules! step { + ($s: expr) => { + match $s { + Ok(output) => output, + Err((e, reason)) => { + close_link(link, reason).await; + return Err(e); + } + } + }; + } + + let isyn_in = SendInitSynIn { + mine_version: manager.config.version, + mine_zid: manager.config.zid, + mine_whatami: manager.config.whatami, + }; + step!(fsm.send_init_syn((&mut state, isyn_in)).await); + + let iack_out = step!(fsm.recv_init_ack(&mut state).await); + + // Open handshake + let osyn_in = SendOpenSynIn { + mine_zid: manager.config.zid, + other_zid: iack_out.other_zid, + mine_lease: manager.config.unicast.lease, + other_cookie: iack_out.other_cookie, + #[cfg(feature = "shared-memory")] + ext_shm: iack_out.ext_shm, + }; + let osyn_out = step!(fsm.send_open_syn((&mut state, osyn_in)).await); + + let oack_out = step!(fsm.recv_open_ack(&mut state).await); + + // Initialize the transport + let config = TransportConfigUnicast { + zid: iack_out.other_zid, + whatami: iack_out.other_whatami, + sn_resolution: state.zenoh.resolution.get(Field::FrameSN), + tx_initial_sn: osyn_out.mine_initial_sn, + is_qos: state.ext_qos.is_qos(), + #[cfg(feature = "transport_multilink")] + multilink: state.ext_mlink.multilink(), + #[cfg(feature = "shared-memory")] + is_shm: state.ext_shm.is_shm(), + }; + + let transport = step!( + manager + .init_transport_unicast(config, link.clone(), LinkUnicastDirection::Outbound) + .await + ); + + // Sync the RX sequence number + let _ = step!(transport + .get_inner() + .map_err(|e| (e, Some(close::reason::INVALID)))) + .sync(oack_out.other_initial_sn) + .await; + + let output = InputFinalize { + transport, + other_lease: oack_out.other_lease, + agreed_batch_size: state.zenoh.batch_size, + }; + let transport = output.transport.clone(); + let res = finalize_transport(link, manager, output).await; + if let Err(e) = res { + let _ = transport.close().await; + return Err(e); + } + + log::debug!( + "New transport link opened from {} to {}: {}", + manager.config.zid, + iack_out.other_zid, + link + ); + + Ok(transport) +} diff --git a/io/zenoh-transport/src/unicast/establishment/open/init_ack.rs b/io/zenoh-transport/src/unicast/establishment/open/init_ack.rs deleted file mode 100644 index 364fba8b4d..0000000000 --- a/io/zenoh-transport/src/unicast/establishment/open/init_ack.rs +++ /dev/null @@ -1,183 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use crate::unicast::establishment::open::OResult; -use crate::unicast::establishment::{ - authenticator::AuthenticatedPeerLink, EstablishmentProperties, -}; -use crate::TransportManager; -use std::convert::TryFrom; -use zenoh_buffers::ZSlice; -use zenoh_core::zasyncread; -use zenoh_link::LinkUnicast; -use zenoh_protocol::{ - common::Attachment, - core::{Property, WhatAmI, ZInt, ZenohId}, - transport::{tmsg, Close, TransportBody}, -}; -use zenoh_result::zerror; - -#[cfg(feature = "shared-memory")] -use crate::unicast::establishment::authenticator::PeerAuthenticatorId; - -/*************************************/ -/* OPEN */ -/*************************************/ -pub(super) struct Output { - pub(super) zid: ZenohId, - pub(super) whatami: WhatAmI, - pub(super) sn_resolution: ZInt, - pub(super) is_qos: bool, - pub(super) is_shm: bool, - pub(super) cookie: ZSlice, - pub(super) open_syn_attachment: Option, -} -pub(super) async fn recv( - link: &LinkUnicast, - manager: &TransportManager, - auth_link: &mut AuthenticatedPeerLink, - _input: super::init_syn::Output, -) -> OResult { - // Wait to read an InitAck - let mut messages = link - .read_transport_message() - .await - .map_err(|e| (e, Some(tmsg::close_reason::INVALID)))?; - if messages.len() != 1 { - return Err(( - zerror!( - "Received multiple messages in response to an InitSyn on {}: {:?}", - link, - messages, - ) - .into(), - Some(tmsg::close_reason::INVALID), - )); - } - - let mut msg = messages.remove(0); - let init_ack = match msg.body { - TransportBody::InitAck(init_ack) => init_ack, - TransportBody::Close(Close { reason, .. }) => { - let e = zerror!( - "Received a close message (reason {}) in response to an InitSyn on: {}", - tmsg::close_reason_to_str(reason), - link, - ); - match reason { - tmsg::close_reason::MAX_LINKS => log::debug!("{}", e), - _ => log::error!("{}", e), - } - return Err((e.into(), None)); - } - _ => { - let e = zerror!( - "Received an invalid message in response to an InitSyn on {}: {:?}", - link, - msg.body - ); - log::error!("{}", e); - return Err((e.into(), Some(tmsg::close_reason::INVALID))); - } - }; - - let sn_resolution = match init_ack.sn_resolution { - Some(sn_resolution) => { - if sn_resolution > manager.config.sn_resolution { - return Err(( - zerror!( - "Rejecting InitAck on {}. Invalid sn resolution: {}", - link, - sn_resolution - ) - .into(), - Some(tmsg::close_reason::INVALID), - )); - } - sn_resolution - } - None => manager.config.sn_resolution, - }; - - // Store the peer id associate do this link - auth_link.peer_id = Some(init_ack.zid); - - let mut init_ack_properties = match msg.attachment.take() { - Some(att) => EstablishmentProperties::try_from(&att) - .map_err(|e| (e, Some(tmsg::close_reason::INVALID)))?, - None => EstablishmentProperties::new(), - }; - - #[allow(unused_mut)] - let mut is_shm = false; - let mut ps_attachment = EstablishmentProperties::new(); - for pa in zasyncread!(manager.state.unicast.peer_authenticator).iter() { - #[allow(unused_mut)] - let mut att = pa - .handle_init_ack( - auth_link, - &init_ack.zid, - sn_resolution, - init_ack_properties.remove(pa.id().into()).map(|x| x.value), - ) - .await; - - #[cfg(feature = "shared-memory")] - if pa.id() == PeerAuthenticatorId::Shm { - // Check if SHM has been validated from the other side - att = match att { - Ok(att) => { - is_shm = att.is_some(); - Ok(att) - } - Err(e) => { - if e.is::() { - is_shm = false; - Ok(None) - } else { - Err(e) - } - } - }; - } - - let mut att = att.map_err(|e| (e, Some(tmsg::close_reason::INVALID)))?; - if let Some(att) = att.take() { - ps_attachment - .insert(Property { - key: pa.id().into(), - value: att, - }) - .map_err(|e| (e, Some(tmsg::close_reason::UNSUPPORTED)))?; - } - } - - let open_syn_attachment = if ps_attachment.is_empty() { - None - } else { - let att = Attachment::try_from(&ps_attachment) - .map_err(|e| (e, Some(tmsg::close_reason::INVALID)))?; - Some(att) - }; - - let output = Output { - zid: init_ack.zid, - whatami: init_ack.whatami, - sn_resolution, - is_qos: init_ack.is_qos, - is_shm, - cookie: init_ack.cookie, - open_syn_attachment, - }; - Ok(output) -} diff --git a/io/zenoh-transport/src/unicast/establishment/open/init_syn.rs b/io/zenoh-transport/src/unicast/establishment/open/init_syn.rs deleted file mode 100644 index be93199a68..0000000000 --- a/io/zenoh-transport/src/unicast/establishment/open/init_syn.rs +++ /dev/null @@ -1,78 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use super::OResult; -use crate::unicast::establishment::{ - authenticator::AuthenticatedPeerLink, EstablishmentProperties, -}; -use crate::TransportManager; -use std::convert::TryFrom; -use zenoh_core::zasyncread; -use zenoh_link::LinkUnicast; -use zenoh_protocol::common::Attachment; -use zenoh_protocol::{ - core::Property, - transport::{tmsg, TransportMessage}, -}; - -/*************************************/ -/* OPEN */ -/*************************************/ -pub(super) struct Output; - -pub(super) async fn send( - link: &LinkUnicast, - manager: &TransportManager, - auth_link: &mut AuthenticatedPeerLink, -) -> OResult { - let mut ps_attachment = EstablishmentProperties::new(); - for pa in zasyncread!(manager.state.unicast.peer_authenticator).iter() { - let mut att = pa - .get_init_syn_properties(auth_link, &manager.config.zid) - .await - .map_err(|e| (e, Some(tmsg::close_reason::UNSUPPORTED)))?; - if let Some(att) = att.take() { - ps_attachment - .insert(Property { - key: pa.id().into(), - value: att, - }) - .map_err(|e| (e, Some(tmsg::close_reason::UNSUPPORTED)))?; - } - } - - // Build and send the InitSyn message - let init_syn_attachment = if ps_attachment.is_empty() { - None - } else { - let att = Attachment::try_from(&ps_attachment) - .map_err(|e| (e, Some(tmsg::close_reason::INVALID)))?; - Some(att) - }; - - let message = TransportMessage::make_init_syn( - manager.config.version, - manager.config.whatami, - manager.config.zid, - manager.config.sn_resolution, - manager.config.unicast.is_qos, - init_syn_attachment, - ); - let _ = link - .write_transport_message(&message) - .await - .map_err(|e| (e, Some(tmsg::close_reason::GENERIC)))?; - - let output = Output; - Ok(output) -} diff --git a/io/zenoh-transport/src/unicast/establishment/open/mod.rs b/io/zenoh-transport/src/unicast/establishment/open/mod.rs deleted file mode 100644 index c285213f7a..0000000000 --- a/io/zenoh-transport/src/unicast/establishment/open/mod.rs +++ /dev/null @@ -1,133 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -mod init_ack; -mod init_syn; -mod open_ack; -mod open_syn; - -use super::authenticator::AuthenticatedPeerLink; -use crate::unicast::establishment::{close_link, transport_finalize, InputFinalize, InputInit}; -use crate::{TransportManager, TransportUnicast}; -use zenoh_link::{LinkUnicast, LinkUnicastDirection}; -use zenoh_protocol::transport::tmsg; -use zenoh_result::ZResult; - -type OError = (zenoh_result::Error, Option); -type OResult = Result; - -pub(crate) async fn open_link( - link: &LinkUnicast, - manager: &TransportManager, - auth_link: &mut AuthenticatedPeerLink, -) -> ZResult { - // INIT handshake - macro_rules! step { - ($s: expr) => { - match $s { - Ok(output) => output, - Err((e, reason)) => { - close_link(link, manager, auth_link, reason).await; - return Err(e); - } - } - }; - } - - let output = step!(init_syn::send(link, manager, auth_link).await); - let output = step!(init_ack::recv(link, manager, auth_link, output).await); - - // Initialize the transport - macro_rules! step { - ($s: expr) => { - match $s { - Ok(output) => output, - Err(e) => { - close_link(link, manager, auth_link, Some(tmsg::close_reason::INVALID)).await; - return Err(e); - } - } - }; - } - - let zid = output.zid; - let input = InputInit { - zid, - whatami: output.whatami, - sn_resolution: output.sn_resolution, - is_shm: output.is_shm, - is_qos: output.is_qos, - }; - let transport = step!(super::transport_init(manager, input).await); - - // OPEN handshake - macro_rules! step { - ($s: expr) => { - match $s { - Ok(output) => output, - Err((e, reason)) => { - if let Ok(ll) = transport.get_links() { - if ll.is_empty() { - let _ = manager.del_transport_unicast(&zid).await; - } - } - close_link(link, manager, auth_link, reason).await; - return Err(e); - } - } - }; - } - - let initial_sn = step!(transport - .get_inner() - .map_err(|e| (e, Some(tmsg::close_reason::INVALID)))) - .config - .initial_sn_tx; - let input = open_syn::Input { - cookie: output.cookie, - initial_sn, - attachment: output.open_syn_attachment, - }; - let output = step!(open_syn::send(link, manager, auth_link, input).await); - let output = step!(open_ack::recv(link, manager, auth_link, output).await); - - // Finalize the transport - // Add the link to the transport - step!(step!(transport - .get_inner() - .map_err(|e| (e, Some(tmsg::close_reason::INVALID)))) - .add_link(link.clone(), LinkUnicastDirection::Outbound) - .map_err(|e| (e, Some(tmsg::close_reason::MAX_LINKS)))); - - // Sync the RX sequence number - let _ = step!(transport - .get_inner() - .map_err(|e| (e, Some(tmsg::close_reason::INVALID)))) - .sync(output.initial_sn) - .await; - - log::debug!("New transport link established with {}: {}", zid, link); - - let output = InputFinalize { - transport, - lease: output.lease, - }; - let transport = output.transport.clone(); - let res = transport_finalize(link, manager, output).await; - if let Err(e) = res { - let _ = transport.close().await; - return Err(e); - } - - Ok(transport) -} diff --git a/io/zenoh-transport/src/unicast/establishment/open/open_ack.rs b/io/zenoh-transport/src/unicast/establishment/open/open_ack.rs deleted file mode 100644 index b68526eb04..0000000000 --- a/io/zenoh-transport/src/unicast/establishment/open/open_ack.rs +++ /dev/null @@ -1,103 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use crate::{ - unicast::establishment::{ - authenticator::AuthenticatedPeerLink, open::OResult, EstablishmentProperties, - }, - TransportManager, -}; -use std::{convert::TryFrom, time::Duration}; -use zenoh_core::zasyncread; -use zenoh_link::LinkUnicast; -use zenoh_protocol::{ - core::ZInt, - transport::{tmsg, Close, TransportBody}, -}; -use zenoh_result::zerror; - -pub(super) struct Output { - pub(super) initial_sn: ZInt, - pub(super) lease: Duration, -} - -pub(super) async fn recv( - link: &LinkUnicast, - manager: &TransportManager, - auth_link: &AuthenticatedPeerLink, - _input: super::open_syn::Output, -) -> OResult { - // Wait to read an OpenAck - let mut messages = link - .read_transport_message() - .await - .map_err(|e| (e, Some(tmsg::close_reason::INVALID)))?; - if messages.len() != 1 { - return Err(( - zerror!( - "Received multiple messages in response to an OpenSyn on {}: {:?}", - link, - messages, - ) - .into(), - Some(tmsg::close_reason::INVALID), - )); - } - - let mut msg = messages.remove(0); - let open_ack = match msg.body { - TransportBody::OpenAck(open_ack) => open_ack, - TransportBody::Close(Close { reason, .. }) => { - let e = zerror!( - "Received a close message (reason {}) in response to an OpenSyn on: {:?}", - tmsg::close_reason_to_str(reason), - link, - ); - match reason { - tmsg::close_reason::MAX_LINKS => log::debug!("{}", e), - _ => log::error!("{}", e), - } - return Err((e.into(), None)); - } - _ => { - let e = zerror!( - "Received an invalid message in response to an OpenSyn on {}: {:?}", - link, - msg.body - ); - log::error!("{}", e); - return Err((e.into(), Some(tmsg::close_reason::INVALID))); - } - }; - - let mut opean_ack_properties = match msg.attachment.take() { - Some(att) => EstablishmentProperties::try_from(&att) - .map_err(|e| (e, Some(tmsg::close_reason::INVALID)))?, - None => EstablishmentProperties::new(), - }; - for pa in zasyncread!(manager.state.unicast.peer_authenticator).iter() { - let _ = pa - .handle_open_ack( - auth_link, - opean_ack_properties.remove(pa.id().into()).map(|x| x.value), - ) - .await - .map_err(|e| (e, Some(tmsg::close_reason::INVALID)))?; - } - - let output = Output { - initial_sn: open_ack.initial_sn, - lease: open_ack.lease, - }; - Ok(output) -} diff --git a/io/zenoh-transport/src/unicast/establishment/open/open_syn.rs b/io/zenoh-transport/src/unicast/establishment/open/open_syn.rs deleted file mode 100644 index c2144249a2..0000000000 --- a/io/zenoh-transport/src/unicast/establishment/open/open_syn.rs +++ /dev/null @@ -1,50 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use super::super::authenticator::AuthenticatedPeerLink; -use super::OResult; -use crate::TransportManager; -use zenoh_buffers::ZSlice; -use zenoh_link::LinkUnicast; -use zenoh_protocol::{ - common::Attachment, - core::ZInt, - transport::{tmsg, TransportMessage}, -}; - -pub(super) struct Input { - pub(super) cookie: ZSlice, - pub(super) initial_sn: ZInt, - pub(super) attachment: Option, -} - -pub(super) struct Output; - -pub(super) async fn send( - link: &LinkUnicast, - manager: &TransportManager, - _auth_link: &AuthenticatedPeerLink, - input: Input, -) -> OResult { - // Build and send an OpenSyn message - let lease = manager.config.unicast.lease; - let message = - TransportMessage::make_open_syn(lease, input.initial_sn, input.cookie, input.attachment); - let _ = link - .write_transport_message(&message) - .await - .map_err(|e| (e, Some(tmsg::close_reason::GENERIC)))?; - - let output = Output; - Ok(output) -} diff --git a/io/zenoh-transport/src/unicast/establishment/properties.rs b/io/zenoh-transport/src/unicast/establishment/properties.rs index c40ae45798..e259b650ab 100644 --- a/io/zenoh-transport/src/unicast/establishment/properties.rs +++ b/io/zenoh-transport/src/unicast/establishment/properties.rs @@ -16,11 +16,8 @@ use std::{ ops::{Deref, DerefMut}, }; use zenoh_buffers::{reader::HasReader, writer::HasWriter, ZBuf}; -use zenoh_codec::{RCodec, WCodec, Zenoh060}; -use zenoh_protocol::{ - common::Attachment, - core::{Property, ZInt}, -}; +use zenoh_codec::{RCodec, WCodec, Zenoh080}; +use zenoh_protocol::core::Property; use zenoh_result::{bail, zerror, Error as ZError, ZResult}; #[derive(Clone, Debug, PartialEq, Eq)] @@ -53,7 +50,7 @@ impl EstablishmentProperties { Ok(()) } - pub(super) fn remove(&mut self, key: ZInt) -> Option { + pub(super) fn remove(&mut self, key: u64) -> Option { self.0 .iter() .position(|x| x.key == key) @@ -69,9 +66,9 @@ impl TryFrom<&EstablishmentProperties> for Attachment { bail!("Can not create an attachment with zero properties") } - let mut zbuf = ZBuf::default(); + let mut zbuf = ZBuf::empty(); let mut writer = zbuf.writer(); - let codec = Zenoh060::default(); + let codec = Zenoh080::new(); codec .write(&mut writer, eps.0.as_slice()) @@ -100,7 +97,7 @@ impl TryFrom<&Attachment> for EstablishmentProperties { fn try_from(att: &Attachment) -> Result { let mut reader = att.buffer.reader(); - let codec = Zenoh060::default(); + let codec = Zenoh080::new(); let ps: Vec = codec.read(&mut reader).map_err(|_| zerror!(""))?; EstablishmentProperties::try_from(ps) @@ -120,7 +117,7 @@ impl EstablishmentProperties { let mut eps = EstablishmentProperties::new(); for _ in MIN..=MAX { loop { - let key: ZInt = rng.gen(); + let key: u64 = rng.gen(); let mut value = vec![0u8; rng.gen_range(MIN..=MAX)]; rng.fill(&mut value[..]); let p = Property { key, value }; diff --git a/io/zenoh-transport/src/unicast/manager.rs b/io/zenoh-transport/src/unicast/manager.rs index f4ac6e8a07..8ba61150c0 100644 --- a/io/zenoh-transport/src/unicast/manager.rs +++ b/io/zenoh-transport/src/unicast/manager.rs @@ -11,27 +11,33 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::unicast::{ - establishment::authenticator::*, - transport::{TransportUnicastConfig, TransportUnicastInner}, - TransportConfigUnicast, TransportUnicast, +#[cfg(feature = "shared-memory")] +use super::shared_memory_unicast::SharedMemoryUnicast; +#[cfg(feature = "shared-memory")] +use super::shm::transport::TransportUnicastShm; +#[cfg(feature = "transport_auth")] +use crate::unicast::establishment::ext::auth::Auth; +#[cfg(feature = "transport_multilink")] +use crate::unicast::establishment::ext::multilink::MultiLink; +use crate::{ + net::transport::TransportUnicastNet, + transport_unicast_inner::TransportUnicastTrait, + unicast::{TransportConfigUnicast, TransportUnicast}, + TransportManager, }; -use crate::TransportManager; -use async_std::prelude::FutureExt; -use async_std::sync::{Mutex as AsyncMutex, RwLock as AsyncRwLock}; -use async_std::task; -use std::collections::{HashMap, HashSet}; -use std::sync::{Arc, Mutex}; -use std::time::Duration; -use zenoh_cfg_properties::config::*; -use zenoh_config::Config; -use zenoh_core::{zasynclock, zasyncread, zasyncwrite, zlock, zparse}; +use async_std::{prelude::FutureExt, sync::Mutex, task}; +use std::{collections::HashMap, sync::Arc, time::Duration}; +#[cfg(feature = "shared-memory")] +use zenoh_config::SharedMemoryConf; +use zenoh_config::{Config, LinkTxConf, QoSConf, TransportUnicastConf}; +use zenoh_core::{zasynclock, zcondfeat}; +use zenoh_crypto::PseudoRng; use zenoh_link::*; use zenoh_protocol::{ - core::{endpoint::Protocol, ZenohId}, - transport::tmsg, + core::{endpoint, ZenohId}, + transport::close, }; -use zenoh_result::{bail, zerror, ZResult}; +use zenoh_result::{bail, zerror, Error, ZResult}; /*************************************/ /* TRANSPORT CONFIG */ @@ -42,8 +48,9 @@ pub struct TransportManagerConfigUnicast { pub accept_timeout: Duration, pub accept_pending: usize, pub max_sessions: usize, - pub max_links: usize, pub is_qos: bool, + #[cfg(feature = "transport_multilink")] + pub max_links: usize, #[cfg(feature = "shared-memory")] pub is_shm: bool, #[cfg(all(feature = "unstable", feature = "transport_compression"))] @@ -52,15 +59,20 @@ pub struct TransportManagerConfigUnicast { pub struct TransportManagerStateUnicast { // Incoming uninitialized transports - pub(super) incoming: Arc>, - // Active peer authenticators - pub(super) peer_authenticator: Arc>>, - // Active link authenticators - pub(super) link_authenticator: Arc>>, + pub(super) incoming: Arc>, // Established listeners pub(super) protocols: Arc>>, // Established transports - pub(super) transports: Arc>>>, + pub(super) transports: Arc>>>, + // Multilink + #[cfg(feature = "transport_multilink")] + pub(super) multilink: Arc, + // Active authenticators + #[cfg(feature = "transport_auth")] + pub(super) authenticator: Arc, + // Shared memory + #[cfg(feature = "shared-memory")] + pub(super) shm: Arc, } pub struct TransportManagerParamsUnicast { @@ -79,14 +91,13 @@ pub struct TransportManagerBuilderUnicast { pub(super) accept_timeout: Duration, pub(super) accept_pending: usize, pub(super) max_sessions: usize, - pub(super) max_links: usize, pub(super) is_qos: bool, + #[cfg(feature = "transport_multilink")] + pub(super) max_links: usize, #[cfg(feature = "shared-memory")] pub(super) is_shm: bool, - #[cfg(all(feature = "unstable", feature = "transport_compression"))] - pub(super) is_compressed: bool, - pub(super) peer_authenticator: HashSet, - pub(super) link_authenticator: HashSet, + #[cfg(feature = "transport_auth")] + pub(super) authenticator: Auth, } impl TransportManagerBuilderUnicast { @@ -115,23 +126,20 @@ impl TransportManagerBuilderUnicast { self } - pub fn max_links(mut self, max_links: usize) -> Self { - self.max_links = max_links; - self - } - - pub fn peer_authenticator(mut self, peer_authenticator: HashSet) -> Self { - self.peer_authenticator = peer_authenticator; + pub fn qos(mut self, is_qos: bool) -> Self { + self.is_qos = is_qos; self } - pub fn link_authenticator(mut self, link_authenticator: HashSet) -> Self { - self.link_authenticator = link_authenticator; + #[cfg(feature = "transport_multilink")] + pub fn max_links(mut self, max_links: usize) -> Self { + self.max_links = max_links; self } - pub fn qos(mut self, is_qos: bool) -> Self { - self.is_qos = is_qos; + #[cfg(feature = "transport_auth")] + pub fn authenticator(mut self, authenticator: Auth) -> Self { + self.authenticator = authenticator; self } @@ -149,35 +157,35 @@ impl TransportManagerBuilderUnicast { pub async fn from_config(mut self, config: &Config) -> ZResult { self = self.lease(Duration::from_millis( - config.transport().link().tx().lease().unwrap(), + *config.transport().link().tx().lease(), )); - self = self.keep_alive(config.transport().link().tx().keep_alive().unwrap()); + self = self.keep_alive(*config.transport().link().tx().keep_alive()); self = self.accept_timeout(Duration::from_millis( - config.transport().unicast().accept_timeout().unwrap(), + *config.transport().unicast().accept_timeout(), )); - self = self.accept_pending(config.transport().unicast().accept_pending().unwrap()); - self = self.max_sessions(config.transport().unicast().max_sessions().unwrap()); - self = self.max_links(config.transport().unicast().max_links().unwrap()); + self = self.accept_pending(*config.transport().unicast().accept_pending()); + self = self.max_sessions(*config.transport().unicast().max_sessions()); self = self.qos(*config.transport().qos().enabled()); + #[cfg(feature = "transport_multilink")] + { + self = self.max_links(*config.transport().unicast().max_links()); + } #[cfg(feature = "shared-memory")] { self = self.shm(*config.transport().shared_memory().enabled()); } - - #[cfg(all(feature = "unstable", feature = "transport_compression"))] + #[cfg(feature = "transport_auth")] { - self = self.compression(*config.transport().link().compression().enabled()); + self = self.authenticator(Auth::from_config(config).await?); } - self = self.peer_authenticator(PeerAuthenticator::from_config(config).await?); - self = self.link_authenticator(LinkAuthenticator::from_config(config).await?); Ok(self) } pub fn build( - #[allow(unused_mut)] // auth_pubkey and shared-memory features require mut - mut self, + self, + #[allow(unused)] prng: &mut PseudoRng, // Required for #[cfg(feature = "transport_multilink")] ) -> ZResult { let config = TransportManagerConfigUnicast { lease: self.lease, @@ -185,42 +193,25 @@ impl TransportManagerBuilderUnicast { accept_timeout: self.accept_timeout, accept_pending: self.accept_pending, max_sessions: self.max_sessions, - max_links: self.max_links, is_qos: self.is_qos, + #[cfg(feature = "transport_multilink")] + max_links: self.max_links, #[cfg(feature = "shared-memory")] is_shm: self.is_shm, #[cfg(all(feature = "unstable", feature = "transport_compression"))] is_compressed: self.is_compressed, }; - // Enable pubkey authentication by default to avoid ZenohId spoofing - #[cfg(feature = "auth_pubkey")] - if !self - .peer_authenticator - .iter() - .any(|a| a.id() == PeerAuthenticatorId::PublicKey) - { - self.peer_authenticator - .insert(PubKeyAuthenticator::make()?.into()); - } - - #[cfg(feature = "shared-memory")] - if self.is_shm - && !self - .peer_authenticator - .iter() - .any(|a| a.id() == PeerAuthenticatorId::Shm) - { - self.peer_authenticator - .insert(SharedMemoryAuthenticator::make()?.into()); - } - let state = TransportManagerStateUnicast { - incoming: Arc::new(AsyncMutex::new(0)), + incoming: Arc::new(Mutex::new(0)), protocols: Arc::new(Mutex::new(HashMap::new())), transports: Arc::new(Mutex::new(HashMap::new())), - link_authenticator: Arc::new(AsyncRwLock::new(self.link_authenticator)), - peer_authenticator: Arc::new(AsyncRwLock::new(self.peer_authenticator)), + #[cfg(feature = "transport_multilink")] + multilink: Arc::new(MultiLink::make(prng)?), + #[cfg(feature = "shared-memory")] + shm: Arc::new(SharedMemoryUnicast::make()?), + #[cfg(feature = "transport_auth")] + authenticator: Arc::new(self.authenticator), }; let params = TransportManagerParamsUnicast { config, state }; @@ -231,20 +222,25 @@ impl TransportManagerBuilderUnicast { impl Default for TransportManagerBuilderUnicast { fn default() -> Self { + let transport = TransportUnicastConf::default(); + let link_tx = LinkTxConf::default(); + let qos = QoSConf::default(); + #[cfg(feature = "shared-memory")] + let shm = SharedMemoryConf::default(); + Self { - lease: Duration::from_millis(zparse!(ZN_LINK_LEASE_DEFAULT).unwrap()), - keep_alive: zparse!(ZN_LINK_KEEP_ALIVE_DEFAULT).unwrap(), - accept_timeout: Duration::from_millis(zparse!(ZN_OPEN_TIMEOUT_DEFAULT).unwrap()), - accept_pending: zparse!(ZN_OPEN_INCOMING_PENDING_DEFAULT).unwrap(), - max_sessions: zparse!(ZN_MAX_SESSIONS_UNICAST_DEFAULT).unwrap(), - max_links: zparse!(ZN_MAX_LINKS_DEFAULT).unwrap(), - is_qos: zparse!(ZN_QOS_DEFAULT).unwrap(), + lease: Duration::from_millis(*link_tx.lease()), + keep_alive: *link_tx.keep_alive(), + accept_timeout: Duration::from_millis(*transport.accept_timeout()), + accept_pending: *transport.accept_pending(), + max_sessions: *transport.max_sessions(), + is_qos: *qos.enabled(), + #[cfg(feature = "transport_multilink")] + max_links: *transport.max_links(), #[cfg(feature = "shared-memory")] - is_shm: zparse!(ZN_SHM_DEFAULT).unwrap(), - #[cfg(all(feature = "unstable", feature = "transport_compression"))] - is_compressed: false, - peer_authenticator: HashSet::new(), - link_authenticator: HashSet::new(), + is_shm: *shm.enabled(), + #[cfg(feature = "transport_auth")] + authenticator: Auth::default(), } } } @@ -257,21 +253,15 @@ impl TransportManager { TransportManagerBuilderUnicast::default() } + #[cfg(feature = "shared-memory")] + pub(crate) fn shm(&self) -> &Arc { + &self.state.unicast.shm + } + pub async fn close_unicast(&self) { log::trace!("TransportManagerUnicast::clear())"); - let mut la_guard = zasyncwrite!(self.state.unicast.link_authenticator); - let mut pa_guard = zasyncwrite!(self.state.unicast.peer_authenticator); - - for la in la_guard.drain() { - la.close().await; - } - - for pa in pa_guard.drain() { - pa.close().await; - } - - let mut pl_guard = zlock!(self.state.unicast.protocols) + let mut pl_guard = zasynclock!(self.state.unicast.protocols) .drain() .map(|(_, v)| v) .collect::>>(); @@ -282,34 +272,40 @@ impl TransportManager { } } - let mut tu_guard = zlock!(self.state.unicast.transports) + let mut tu_guard = zasynclock!(self.state.unicast.transports) .drain() .map(|(_, v)| v) - .collect::>>(); + .collect::>>(); for tu in tu_guard.drain(..) { - let _ = tu.close(tmsg::close_reason::GENERIC).await; + let _ = tu.close(close::reason::GENERIC).await; } } /*************************************/ /* LINK MANAGER */ /*************************************/ - fn new_link_manager_unicast(&self, protocol: &Protocol) -> ZResult { - let mut w_guard = zlock!(self.state.unicast.protocols); - if let Some(lm) = w_guard.get(protocol.as_str()) { + async fn new_link_manager_unicast(&self, protocol: &str) -> ZResult { + if !self.config.protocols.iter().any(|x| x.as_str() == protocol) { + bail!( + "Unsupported protocol: {}. Supported protocols are: {:?}", + protocol, + self.config.protocols + ); + } + + let mut w_guard = zasynclock!(self.state.unicast.protocols); + if let Some(lm) = w_guard.get(protocol) { Ok(lm.clone()) } else { - let lm = LinkManagerBuilderUnicast::make( - self.new_unicast_link_sender.clone(), - protocol.as_str(), - )?; + let lm = + LinkManagerBuilderUnicast::make(self.new_unicast_link_sender.clone(), protocol)?; w_guard.insert(protocol.to_string(), lm.clone()); Ok(lm) } } - fn get_link_manager_unicast(&self, protocol: &Protocol) -> ZResult { - match zlock!(self.state.unicast.protocols).get(protocol.as_str()) { + async fn get_link_manager_unicast(&self, protocol: &str) -> ZResult { + match zasynclock!(self.state.unicast.protocols).get(protocol) { Some(manager) => Ok(manager.clone()), None => bail!( "Can not get the link manager for protocol ({}) because it has not been found", @@ -318,8 +314,8 @@ impl TransportManager { } } - fn del_link_manager_unicast(&self, protocol: &Protocol) -> ZResult<()> { - match zlock!(self.state.unicast.protocols).remove(protocol.as_str()) { + async fn del_link_manager_unicast(&self, protocol: &str) -> ZResult<()> { + match zasynclock!(self.state.unicast.protocols).remove(protocol) { Some(_) => Ok(()), None => bail!( "Can not delete the link manager for protocol ({}) because it has not been found.", @@ -332,34 +328,52 @@ impl TransportManager { /* LISTENER */ /*************************************/ pub async fn add_listener_unicast(&self, mut endpoint: EndPoint) -> ZResult { - let manager = self.new_link_manager_unicast(&endpoint.protocol())?; + if self + .locator_inspector + .is_multicast(&endpoint.to_locator()) + .await? + { + bail!( + "Can not listen on unicast endpoint with a multicast endpoint: {}.", + endpoint + ) + } + + let manager = self + .new_link_manager_unicast(endpoint.protocol().as_str()) + .await?; // Fill and merge the endpoint configuration - if let Some(config) = self.config.endpoint.get(endpoint.protocol().as_str()) { - endpoint.config_mut().extend(config.iter())?; + if let Some(config) = self.config.endpoints.get(endpoint.protocol().as_str()) { + endpoint + .config_mut() + .extend(endpoint::Parameters::iter(config))?; }; manager.new_listener(endpoint).await } pub async fn del_listener_unicast(&self, endpoint: &EndPoint) -> ZResult<()> { - let lm = self.get_link_manager_unicast(&endpoint.protocol())?; + let lm = self + .get_link_manager_unicast(endpoint.protocol().as_str()) + .await?; lm.del_listener(endpoint).await?; if lm.get_listeners().is_empty() { - self.del_link_manager_unicast(&endpoint.protocol())?; + self.del_link_manager_unicast(endpoint.protocol().as_str()) + .await?; } Ok(()) } - pub fn get_listeners_unicast(&self) -> Vec { + pub async fn get_listeners_unicast(&self) -> Vec { let mut vec: Vec = vec![]; - for p in zlock!(self.state.unicast.protocols).values() { + for p in zasynclock!(self.state.unicast.protocols).values() { vec.extend_from_slice(&p.get_listeners()); } vec } - pub fn get_locators_unicast(&self) -> Vec { + pub async fn get_locators_unicast(&self) -> Vec { let mut vec: Vec = vec![]; - for p in zlock!(self.state.unicast.protocols).values() { + for p in zasynclock!(self.state.unicast.protocols).values() { vec.extend_from_slice(&p.get_locators()); } vec @@ -368,61 +382,38 @@ impl TransportManager { /*************************************/ /* TRANSPORT */ /*************************************/ - pub(super) fn init_transport_unicast( + pub(super) async fn init_transport_unicast( &self, config: TransportConfigUnicast, - ) -> ZResult { - let mut guard = zlock!(self.state.unicast.transports); + link: LinkUnicast, + direction: LinkUnicastDirection, + ) -> Result)> { + let mut guard = zasynclock!(self.state.unicast.transports); // First verify if the transport already exists - match guard.get(&config.peer) { + match guard.get(&config.zid) { Some(transport) => { + let existing_config = transport.get_config(); // If it exists, verify that fundamental parameters like are correct. // Ignore the non fundamental parameters like initial SN. - if transport.config.whatami != config.whatami { - let e = zerror!( - "Transport with peer {} already exist. Invalid whatami: {}. Execpted: {}.", - config.peer, - config.whatami, - transport.config.whatami - ); - log::trace!("{}", e); - return Err(e.into()); - } - - if transport.config.sn_resolution != config.sn_resolution { - let e = zerror!( - "Transport with peer {} already exist. Invalid sn resolution: {}. Execpted: {}.", - config.peer, config.sn_resolution, transport.config.sn_resolution - ); - log::trace!("{}", e); - return Err(e.into()); - } - - #[cfg(feature = "shared-memory")] - if transport.config.is_shm != config.is_shm { + if *existing_config != config { let e = zerror!( - "Transport with peer {} already exist. Invalid is_shm: {}. Execpted: {}.", - config.peer, - config.is_shm, - transport.config.is_shm + "Transport with peer {} already exist. Invalid config: {:?}. Expected: {:?}.", + config.zid, + config, + existing_config ); log::trace!("{}", e); - return Err(e.into()); + return Err((e.into(), Some(close::reason::INVALID))); } - if transport.config.is_qos != config.is_qos { - let e = zerror!( - "Transport with peer {} already exist. Invalid is_qos: {}. Execpted: {}.", - config.peer, - config.is_qos, - transport.config.is_qos - ); - log::trace!("{}", e); - return Err(e.into()); - } + // Add the link to the transport + transport + .add_link(link, direction) + .await + .map_err(|e| (e, Some(close::reason::MAX_LINKS)))?; - Ok(transport.into()) + Ok(TransportUnicast(Arc::downgrade(transport))) } None => { // Then verify that we haven't reached the transport number limit @@ -430,36 +421,93 @@ impl TransportManager { let e = zerror!( "Max transports reached ({}). Denying new transport with peer: {}", self.config.unicast.max_sessions, - config.peer + config.zid ); log::trace!("{}", e); - return Err(e.into()); + return Err((e.into(), Some(close::reason::INVALID))); } // Create the transport - let stc = TransportUnicastConfig { - manager: self.clone(), - zid: config.peer, + let is_multilink = + zcondfeat!("transport_multilink", config.multilink.is_some(), false); + + let stc = TransportConfigUnicast { + zid: config.zid, whatami: config.whatami, sn_resolution: config.sn_resolution, - initial_sn_tx: config.initial_sn_tx, - is_shm: config.is_shm, + tx_initial_sn: config.tx_initial_sn, is_qos: config.is_qos, + #[cfg(feature = "transport_multilink")] + multilink: config.multilink, + #[cfg(feature = "shared-memory")] + is_shm: config.is_shm, }; - let a_t = Arc::new(TransportUnicastInner::make(stc)?); + + async fn make_net_transport( + manager: &TransportManager, + stc: TransportConfigUnicast, + link: LinkUnicast, + direction: LinkUnicastDirection, + ) -> Result, (Error, Option)> { + log::debug!("Will use NET transport!"); + let t: Arc = + TransportUnicastNet::make(manager.clone(), stc) + .map_err(|e| (e, Some(close::reason::INVALID))) + .map(|v| Arc::new(v) as Arc)?; + // Add the link to the transport + t.add_link(link, direction) + .await + .map_err(|e| (e, Some(close::reason::MAX_LINKS)))?; + Ok(t) + } + + // select and create transport implementation depending on the cfg and enabled features + let a_t: Arc = zcondfeat!( + "shared-memory", + { + if stc.is_shm { + log::debug!("Will use SHM transport!"); + TransportUnicastShm::make(self.clone(), stc, link) + .map_err(|e| (e, Some(close::reason::INVALID))) + .map(|v| Arc::new(v) as Arc)? + } else { + make_net_transport(self, stc, link, direction).await? + } + }, + make_net_transport(self, stc, link, direction).await? + ); // Add the transport transport to the list of active transports - let transport: TransportUnicast = (&a_t).into(); - guard.insert(config.peer, a_t); - - log::debug!( - "New transport opened with {}: whatami {}, sn resolution {}, initial sn {:?}, shm: {}, qos: {}", - config.peer, - config.whatami, - config.sn_resolution, - config.initial_sn_tx, - config.is_shm, - config.is_qos + let transport = TransportUnicast(Arc::downgrade(&a_t)); + guard.insert(config.zid, a_t); + + zcondfeat!( + "shared-memory", + { + log::debug!( + "New transport opened between {} and {} - whatami: {}, sn resolution: {:?}, initial sn: {:?}, qos: {}, shm: {}, multilink: {}", + self.config.zid, + config.zid, + config.whatami, + config.sn_resolution, + config.tx_initial_sn, + config.is_qos, + config.is_shm, + is_multilink + ); + }, + { + log::debug!( + "New transport opened between {} and {} - whatami: {}, sn resolution: {:?}, initial sn: {:?}, qos: {}, multilink: {}", + self.config.zid, + config.zid, + config.whatami, + config.sn_resolution, + config.tx_initial_sn, + config.is_qos, + is_multilink + ); + } ); Ok(transport) @@ -471,19 +519,6 @@ impl TransportManager { &self, mut endpoint: EndPoint, ) -> ZResult { - let p = endpoint.protocol(); - if !self - .config - .protocols - .iter() - .any(|x| x.as_str() == p.as_str()) - { - bail!( - "Unsupported protocol: {}. Supported protocols are: {:?}", - p, - self.config.protocols - ); - } if self .locator_inspector .is_multicast(&endpoint.to_locator()) @@ -496,49 +531,51 @@ impl TransportManager { } // Automatically create a new link manager for the protocol if it does not exist - let manager = self.new_link_manager_unicast(&endpoint.protocol())?; + let manager = self + .new_link_manager_unicast(endpoint.protocol().as_str()) + .await?; // Fill and merge the endpoint configuration - if let Some(config) = self.config.endpoint.get(endpoint.protocol().as_str()) { - endpoint.config_mut().extend(config.iter())?; + if let Some(config) = self.config.endpoints.get(endpoint.protocol().as_str()) { + endpoint + .config_mut() + .extend(endpoint::Parameters::iter(config))?; }; // Create a new link associated by calling the Link Manager let link = manager.new_link(endpoint).await?; // Open the link - let mut auth_link = AuthenticatedPeerLink { - src: link.get_src().to_owned(), - dst: link.get_src().to_owned(), - peer_id: None, - }; - super::establishment::open::open_link(&link, self, &mut auth_link).await + super::establishment::open::open_link(&link, self).await } - pub fn get_transport_unicast(&self, peer: &ZenohId) -> Option { - zlock!(self.state.unicast.transports) + pub async fn get_transport_unicast(&self, peer: &ZenohId) -> Option { + zasynclock!(self.state.unicast.transports) .get(peer) - .map(|t| t.into()) + .map(|t| { + // todo: I cannot find a way to make transport.into() work for TransportUnicastTrait + let weak = Arc::downgrade(t); + TransportUnicast(weak) + }) } - pub fn get_transports_unicast(&self) -> Vec { - zlock!(self.state.unicast.transports) + pub async fn get_transports_unicast(&self) -> Vec { + zasynclock!(self.state.unicast.transports) .values() - .map(|t| t.into()) + .map(|t| { + // todo: I cannot find a way to make transport.into() work for TransportUnicastTrait + let weak = Arc::downgrade(t); + TransportUnicast(weak) + }) .collect() } pub(super) async fn del_transport_unicast(&self, peer: &ZenohId) -> ZResult<()> { - let _ = zlock!(self.state.unicast.transports) + zasynclock!(self.state.unicast.transports) .remove(peer) .ok_or_else(|| { let e = zerror!("Can not delete the transport of peer: {}", peer); log::trace!("{}", e); e })?; - - for pa in zasyncread!(self.state.unicast.peer_authenticator).iter() { - pa.handle_close(peer).await; - } - Ok(()) } @@ -560,49 +597,12 @@ impl TransportManager { *guard += 1; drop(guard); - let mut peer_id: Option = None; - let peer_link = Link::from(&link); - for la in zasyncread!(self.state.unicast.link_authenticator).iter() { - let res = la.handle_new_link(&peer_link).await; - match res { - Ok(zid) => { - // Check that all the peer authenticators, eventually return the same ZenohId - if let Some(zid1) = peer_id.as_ref() { - if let Some(zid2) = zid.as_ref() { - if zid1 != zid2 { - log::debug!("Ambigous PeerID identification for link: {}", link); - let _ = link.close().await; - let mut guard = zasynclock!(self.state.unicast.incoming); - *guard -= 1; - return; - } - } - } else { - peer_id = zid; - } - } - Err(e) => { - log::debug!("{}", e); - let mut guard = zasynclock!(self.state.unicast.incoming); - *guard -= 1; - return; - } - } - } - // Spawn a task to accept the link let c_manager = self.clone(); task::spawn(async move { - let mut auth_link = AuthenticatedPeerLink { - src: link.get_src().to_owned(), - dst: link.get_dst().to_owned(), - peer_id, - }; - - if let Err(e) = - super::establishment::accept::accept_link(&link, &c_manager, &mut auth_link) - .timeout(c_manager.config.unicast.accept_timeout) - .await + if let Err(e) = super::establishment::accept::accept_link(&link, &c_manager) + .timeout(c_manager.config.unicast.accept_timeout) + .await { log::debug!("{}", e); let _ = link.close().await; @@ -612,3 +612,10 @@ impl TransportManager { }); } } + +#[cfg(all(feature = "test", feature = "transport_auth"))] +impl TransportManager { + pub fn get_auth_handle_unicast(&self) -> Arc { + self.state.unicast.authenticator.clone() + } +} diff --git a/io/zenoh-transport/src/unicast/mod.rs b/io/zenoh-transport/src/unicast/mod.rs index 2383c67ceb..c7f6038bbe 100644 --- a/io/zenoh-transport/src/unicast/mod.rs +++ b/io/zenoh-transport/src/unicast/mod.rs @@ -12,90 +12,59 @@ // ZettaScale Zenoh Team, // pub mod establishment; -pub(crate) mod link; pub(crate) mod manager; -pub(crate) mod rx; -pub(crate) mod transport; -pub(crate) mod tx; +pub(crate) mod net; +pub(crate) mod transport_unicast_inner; + +#[cfg(feature = "test")] +pub mod test_helpers; + +#[cfg(feature = "shared-memory")] +pub(crate) mod shared_memory_unicast; +#[cfg(feature = "shared-memory")] +pub(crate) mod shm; + +use self::transport_unicast_inner::TransportUnicastTrait; -use super::common; -#[cfg(feature = "stats")] -use super::common::stats::stats_struct; use super::{TransportPeer, TransportPeerEventHandler}; +#[cfg(feature = "transport_multilink")] +use establishment::ext::auth::ZPublicKey; pub use manager::*; use std::fmt; use std::sync::{Arc, Weak}; -use transport::TransportUnicastInner; +use zenoh_core::zcondfeat; use zenoh_link::Link; +use zenoh_protocol::network::NetworkMessage; use zenoh_protocol::{ - core::{WhatAmI, ZInt, ZenohId}, - transport::tmsg, - zenoh::ZenohMessage, + core::{Bits, WhatAmI, ZenohId}, + transport::{close, TransportSn}, }; use zenoh_result::{zerror, ZResult}; -/*************************************/ -/* STATS */ -/*************************************/ -#[cfg(feature = "stats")] -use serde::{Deserialize, Serialize}; -#[cfg(feature = "stats")] -use std::sync::atomic::{AtomicUsize, Ordering}; -#[cfg(feature = "stats")] -stats_struct! { - #[derive(Clone, Debug, Deserialize, Serialize)] - pub struct TransportUnicastStats { - pub tx_t_msgs, - pub tx_z_msgs, - pub tx_z_dropped, - pub tx_z_data_msgs, - pub tx_z_data_payload_bytes, - pub tx_z_data_reply_msgs, - pub tx_z_data_reply_payload_bytes, - pub tx_z_pull_msgs, - pub tx_z_query_msgs, - pub tx_z_declare_msgs, - pub tx_z_linkstate_msgs, - pub tx_z_unit_msgs, - pub tx_z_unit_reply_msgs, - pub tx_bytes, - pub rx_t_msgs, - pub rx_z_msgs, - pub rx_z_data_msgs, - pub rx_z_data_payload_bytes, - pub rx_z_data_reply_msgs, - pub rx_z_data_reply_payload_bytes, - pub rx_z_pull_msgs, - pub rx_z_query_msgs, - pub rx_z_declare_msgs, - pub rx_z_linkstate_msgs, - pub rx_z_unit_msgs, - pub rx_z_unit_reply_msgs, - pub rx_bytes, - } -} - /*************************************/ /* TRANSPORT UNICAST */ /*************************************/ -#[derive(Clone, Copy)] +#[derive(Clone, Debug, PartialEq, Eq)] pub(crate) struct TransportConfigUnicast { - pub(crate) peer: ZenohId, + pub(crate) zid: ZenohId, pub(crate) whatami: WhatAmI, - pub(crate) sn_resolution: ZInt, - pub(crate) initial_sn_tx: ZInt, - pub(crate) is_shm: bool, + pub(crate) sn_resolution: Bits, + pub(crate) tx_initial_sn: TransportSn, pub(crate) is_qos: bool, + #[cfg(feature = "transport_multilink")] + pub(crate) multilink: Option, + #[cfg(feature = "shared-memory")] + pub(crate) is_shm: bool, } /// [`TransportUnicast`] is the transport handler returned /// when opening a new unicast transport #[derive(Clone)] -pub struct TransportUnicast(Weak); +pub struct TransportUnicast(Weak); impl TransportUnicast { #[inline(always)] - pub(super) fn get_inner(&self) -> ZResult> { + pub(super) fn get_inner(&self) -> ZResult> { self.0 .upgrade() .ok_or_else(|| zerror!("Transport unicast closed").into()) @@ -113,24 +82,13 @@ impl TransportUnicast { Ok(transport.get_whatami()) } - #[inline(always)] - pub fn get_sn_resolution(&self) -> ZResult { - let transport = self.get_inner()?; - Ok(transport.get_sn_resolution()) - } - + #[cfg(feature = "shared-memory")] #[inline(always)] pub fn is_shm(&self) -> ZResult { let transport = self.get_inner()?; Ok(transport.is_shm()) } - #[inline(always)] - pub fn is_qos(&self) -> ZResult { - let transport = self.get_inner()?; - Ok(transport.is_qos()) - } - #[inline(always)] pub fn get_callback(&self) -> ZResult>> { let transport = self.get_inner()?; @@ -142,13 +100,14 @@ impl TransportUnicast { let tp = TransportPeer { zid: transport.get_zid(), whatami: transport.get_whatami(), - is_qos: transport.is_qos(), - is_shm: transport.is_shm(), links: transport .get_links() .into_iter() .map(|l| l.into()) .collect(), + is_qos: transport.is_qos(), + #[cfg(feature = "shared-memory")] + is_shm: transport.is_shm(), }; Ok(tp) } @@ -164,10 +123,9 @@ impl TransportUnicast { } #[inline(always)] - pub fn schedule(&self, message: ZenohMessage) -> ZResult<()> { + pub fn schedule(&self, message: NetworkMessage) -> ZResult<()> { let transport = self.get_inner()?; - transport.schedule(message); - Ok(()) + transport.schedule(message) } #[inline(always)] @@ -178,9 +136,7 @@ impl TransportUnicast { .into_iter() .find(|l| l.get_src() == &link.src && l.get_dst() == &link.dst) .ok_or_else(|| zerror!("Invalid link"))?; - transport - .close_link(&link, tmsg::close_reason::GENERIC) - .await?; + transport.close_link(&link, close::reason::GENERIC).await?; Ok(()) } @@ -188,25 +144,20 @@ impl TransportUnicast { pub async fn close(&self) -> ZResult<()> { // Return Ok if the transport has already been closed match self.get_inner() { - Ok(transport) => transport.close(tmsg::close_reason::GENERIC).await, + Ok(transport) => transport.close(close::reason::GENERIC).await, Err(_) => Ok(()), } } - #[inline(always)] - pub fn handle_message(&self, message: ZenohMessage) -> ZResult<()> { - self.schedule(message) - } - #[cfg(feature = "stats")] - pub fn get_stats(&self) -> ZResult { - Ok(self.get_inner()?.stats.snapshot()) + pub fn get_stats(&self) -> ZResult> { + Ok(self.get_inner()?.stats()) } } -impl From<&Arc> for TransportUnicast { - fn from(s: &Arc) -> TransportUnicast { - TransportUnicast(Arc::downgrade(s)) +impl From<&Arc> for TransportUnicast { + fn from(link: &Arc) -> TransportUnicast { + TransportUnicast(Arc::downgrade(link)) } } @@ -221,15 +172,20 @@ impl PartialEq for TransportUnicast { impl fmt::Debug for TransportUnicast { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.get_inner() { - Ok(transport) => f - .debug_struct("Transport Unicast") - .field("zid", &transport.get_zid()) - .field("whatami", &transport.get_whatami()) - .field("sn_resolution", &transport.get_sn_resolution()) - .field("is_qos", &transport.is_qos()) - .field("is_shm", &transport.is_shm()) - .field("links", &transport.get_links()) - .finish(), + Ok(transport) => { + let is_shm = zcondfeat!("shared-memory", transport.is_shm(), false); + + transport + .add_debug_fields( + f.debug_struct("Transport Unicast") + .field("zid", &transport.get_zid()) + .field("whatami", &transport.get_whatami()) + .field("is_qos", &transport.is_qos()) + .field("is_shm", &is_shm) + .field("links", &transport.get_links()), + ) + .finish() + } Err(e) => { write!(f, "{e}") } diff --git a/io/zenoh-transport/src/unicast/link.rs b/io/zenoh-transport/src/unicast/net/link.rs similarity index 89% rename from io/zenoh-transport/src/unicast/link.rs rename to io/zenoh-transport/src/unicast/net/link.rs index 6c99cc627d..144f07db1d 100644 --- a/io/zenoh-transport/src/unicast/link.rs +++ b/io/zenoh-transport/src/unicast/net/link.rs @@ -11,14 +11,14 @@ // Contributors: // ZettaScale Zenoh Team, // -use super::common::conduit::TransportConduitTx; -use super::transport::TransportUnicastInner; -#[cfg(feature = "stats")] -use super::TransportUnicastStatsAtomic; +use super::transport::TransportUnicastNet; use crate::common::pipeline::{ TransmissionPipeline, TransmissionPipelineConf, TransmissionPipelineConsumer, TransmissionPipelineProducer, }; +use crate::common::priority::TransportPriorityTx; +#[cfg(feature = "stats")] +use crate::common::stats::TransportStats; use crate::TransportExecutor; use async_std::prelude::FutureExt; use async_std::task; @@ -28,12 +28,9 @@ use async_std::task::JoinHandle; use std::convert::TryInto; use std::sync::Arc; use std::time::Duration; -use std::vec; -use zenoh_buffers::reader::{HasReader, Reader}; use zenoh_buffers::ZSlice; -use zenoh_codec::{RCodec, Zenoh060}; use zenoh_link::{LinkUnicast, LinkUnicastDirection}; -use zenoh_protocol::transport::TransportMessage; +use zenoh_protocol::transport::{BatchSize, KeepAlive, TransportMessage}; use zenoh_result::{bail, zerror, ZResult}; use zenoh_sync::{RecyclingObjectPool, Signal}; @@ -67,7 +64,7 @@ pub(super) struct TransportLinkUnicast { // The transmission pipeline pub(super) pipeline: Option, // The transport this link is associated to - transport: TransportUnicastInner, + transport: TransportUnicastNet, // The signals to stop TX/RX tasks handle_tx: Option>>, signal_rx: Signal, @@ -76,7 +73,7 @@ pub(super) struct TransportLinkUnicast { impl TransportLinkUnicast { pub(super) fn new( - transport: TransportUnicastInner, + transport: TransportUnicastNet, link: LinkUnicast, direction: LinkUnicastDirection, ) -> TransportLinkUnicast { @@ -98,21 +95,21 @@ impl TransportLinkUnicast { executor: &TransportExecutor, keep_alive: Duration, batch_size: u16, - conduit_tx: &[TransportConduitTx], + priority_tx: &[TransportPriorityTx], ) { if self.handle_tx.is_none() { let config = TransmissionPipelineConf { is_streamed: self.link.is_streamed(), batch_size: batch_size.min(self.link.get_mtu()), - queue_size: self.transport.config.manager.config.queue_size, - backoff: self.transport.config.manager.config.queue_backoff, + queue_size: self.transport.manager.config.queue_size, + backoff: self.transport.manager.config.queue_backoff, }; #[cfg(all(feature = "unstable", feature = "transport_compression"))] let is_compressed = self.transport.config.manager.config.unicast.is_compressed; // The pipeline - let (producer, consumer) = TransmissionPipeline::make(config, conduit_tx); + let (producer, consumer) = TransmissionPipeline::make(config, priority_tx); self.pipeline = Some(producer); // Spawn the TX task @@ -146,13 +143,13 @@ impl TransportLinkUnicast { } } - pub(super) fn start_rx(&mut self, lease: Duration) { + pub(super) fn start_rx(&mut self, lease: Duration, batch_size: u16) { if self.handle_rx.is_none() { // Spawn the RX task let c_link = self.link.clone(); let c_transport = self.transport.clone(); let c_signal = self.signal_rx.clone(); - let c_rx_buffer_size = self.transport.config.manager.config.link_rx_buffer_size; + let c_rx_buffer_size = self.transport.manager.config.link_rx_buffer_size; let handle = task::spawn(async move { // Start the consume task @@ -161,6 +158,7 @@ impl TransportLinkUnicast { c_transport.clone(), lease, c_signal.clone(), + batch_size, c_rx_buffer_size, ) .await; @@ -184,14 +182,14 @@ impl TransportLinkUnicast { log::trace!("{}: closing", self.link); self.stop_rx(); if let Some(handle) = self.handle_rx.take() { - // Safety: it is safe to unwrap the Arc since we have the ownership of the whole link + // SAFETY: it is safe to unwrap the Arc since we have the ownership of the whole link let handle_rx = Arc::try_unwrap(handle).unwrap(); handle_rx.await; } self.stop_tx(); if let Some(handle) = self.handle_tx.take() { - // Safety: it is safe to unwrap the Arc since we have the ownership of the whole link + // SAFETY: it is safe to unwrap the Arc since we have the ownership of the whole link let handle_tx = Arc::try_unwrap(handle).unwrap(); handle_tx.await; } @@ -207,7 +205,7 @@ async fn tx_task( mut pipeline: TransmissionPipelineConsumer, link: LinkUnicast, keep_alive: Duration, - #[cfg(feature = "stats")] stats: Arc, + #[cfg(feature = "stats")] stats: Arc, #[cfg(all(feature = "unstable", feature = "transport_compression"))] is_compressed: bool, ) -> ZResult<()> { #[cfg(all(feature = "unstable", feature = "transport_compression"))] @@ -247,12 +245,10 @@ async fn tx_task( None => break, }, Err(_) => { - let zid = None; - let attachment = None; - let message = TransportMessage::make_keep_alive(zid, attachment); + let message: TransportMessage = KeepAlive.into(); #[allow(unused_variables)] // Used when stats feature is enabled - let n = link.write_transport_message(&message).await?; + let n = link.send(&message).await?; #[cfg(feature = "stats")] { stats.inc_tx_t_msgs(1); @@ -282,9 +278,10 @@ async fn tx_task( async fn rx_task_stream( link: LinkUnicast, - transport: TransportUnicastInner, + transport: TransportUnicastNet, lease: Duration, signal: Signal, + rx_batch_size: BatchSize, rx_buffer_size: usize, ) -> ZResult<()> { enum Action { @@ -296,7 +293,7 @@ async fn rx_task_stream( // 16 bits for reading the batch length let mut length = [0_u8, 0_u8]; link.read_exact(&mut length).await?; - let n = u16::from_le_bytes(length) as usize; + let n = BatchSize::from_le_bytes(length) as usize; link.read_exact(&mut buffer[0..n]).await?; Ok(Action::Read(n)) } @@ -306,10 +303,8 @@ async fn rx_task_stream( Ok(Action::Stop) } - let codec = Zenoh060::default(); - // The pool of buffers - let mtu = link.get_mtu() as usize; + let mtu = link.get_mtu().min(rx_batch_size) as usize; let mut n = rx_buffer_size / mtu; if rx_buffer_size % mtu != 0 { n += 1; @@ -342,20 +337,9 @@ async fn rx_task_stream( rx_decompress(&mut buffer, &pool, n, &mut start_pos, &mut end_pos)?; // Deserialize all the messages from the current ZBuf - let mut zslice = ZSlice::make(Arc::new(buffer), start_pos, end_pos).unwrap(); - let mut reader = zslice.reader(); - while reader.can_read() { - let msg: TransportMessage = codec - .read(&mut reader) - .map_err(|_| zerror!("{}: decoding error", link))?; - - #[cfg(feature = "stats")] - { - transport.stats.inc_rx_t_msgs(1); - } - - transport.receive_message(msg, &link)? - } + let zslice = ZSlice::make(Arc::new(buffer), start_pos, end_pos) + .map_err(|_| zerror!("Read {} bytes but buffer is {} bytes", n, mtu))?; + transport.read_messages(zslice, &link)?; } Action::Stop => break, } @@ -365,9 +349,10 @@ async fn rx_task_stream( async fn rx_task_dgram( link: LinkUnicast, - transport: TransportUnicastInner, + transport: TransportUnicastNet, lease: Duration, signal: Signal, + rx_batch_size: BatchSize, rx_buffer_size: usize, ) -> ZResult<()> { enum Action { @@ -385,10 +370,8 @@ async fn rx_task_dgram( Ok(Action::Stop) } - let codec = Zenoh060::default(); - // The pool of buffers - let mtu = link.get_mtu() as usize; + let mtu = link.get_mtu().min(rx_batch_size) as usize; let mut n = rx_buffer_size / mtu; if rx_buffer_size % mtu != 0 { n += 1; @@ -426,20 +409,9 @@ async fn rx_task_dgram( rx_decompress(&mut buffer, &pool, n, &mut start_pos, &mut end_pos)?; // Deserialize all the messages from the current ZBuf - let mut zslice = ZSlice::make(Arc::new(buffer), start_pos, end_pos).unwrap(); - let mut reader = zslice.reader(); - while reader.can_read() { - let msg: TransportMessage = codec - .read(&mut reader) - .map_err(|_| zerror!("{}: decoding error", link))?; - - #[cfg(feature = "stats")] - { - transport.stats.inc_rx_t_msgs(1); - } - - transport.receive_message(msg, &link)? - } + let zslice = ZSlice::make(Arc::new(buffer), start_pos, end_pos) + .map_err(|_| zerror!("Read {} bytes but buffer is {} bytes", n, mtu))?; + transport.read_messages(zslice, &link)?; } Action::Stop => break, } @@ -449,15 +421,32 @@ async fn rx_task_dgram( async fn rx_task( link: LinkUnicast, - transport: TransportUnicastInner, + transport: TransportUnicastNet, lease: Duration, signal: Signal, + rx_batch_size: u16, rx_buffer_size: usize, ) -> ZResult<()> { if link.is_streamed() { - rx_task_stream(link, transport, lease, signal, rx_buffer_size).await + rx_task_stream( + link, + transport, + lease, + signal, + rx_batch_size, + rx_buffer_size, + ) + .await } else { - rx_task_dgram(link, transport, lease, signal, rx_buffer_size).await + rx_task_dgram( + link, + transport, + lease, + signal, + rx_batch_size, + rx_buffer_size, + ) + .await } } diff --git a/io/zenoh-transport/src/unicast/net/mod.rs b/io/zenoh-transport/src/unicast/net/mod.rs new file mode 100644 index 0000000000..7cb4274baa --- /dev/null +++ b/io/zenoh-transport/src/unicast/net/mod.rs @@ -0,0 +1,18 @@ +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +pub(crate) mod transport; + +mod link; +mod rx; +mod tx; diff --git a/io/zenoh-transport/src/unicast/reliability.rs b/io/zenoh-transport/src/unicast/net/reliability.rs similarity index 88% rename from io/zenoh-transport/src/unicast/reliability.rs rename to io/zenoh-transport/src/unicast/net/reliability.rs index 339e23ec0e..2f5f6db5c3 100644 --- a/io/zenoh-transport/src/unicast/reliability.rs +++ b/io/zenoh-transport/src/unicast/net/reliability.rs @@ -15,7 +15,7 @@ use std::convert::TryInto; use std::fmt; use super::common::seq_num::SeqNum; -use super::core::ZInt; +use super::core::u64; use zenoh_result::{ZError, ZErrorKind, ZResult}; use zenoh_util::zerror; @@ -28,11 +28,7 @@ pub(super) struct ReliabilityQueue { } impl ReliabilityQueue { - pub(super) fn new( - capacity: usize, - initial_sn: ZInt, - sn_resolution: ZInt, - ) -> ReliabilityQueue { + pub(super) fn new(capacity: usize, initial_sn: u64, sn_resolution: u64) -> ReliabilityQueue { let mut inner = Vec::with_capacity(capacity); for _ in 0..capacity { inner.push(None); @@ -67,11 +63,11 @@ impl ReliabilityQueue { } #[inline] - pub(super) fn get_base(&self) -> ZInt { + pub(super) fn get_base(&self) -> u64 { self.sn.get() } - pub(super) fn set_base(&mut self, sn: ZInt) -> ZResult<()> { + pub(super) fn set_base(&mut self, sn: u64) -> ZResult<()> { let gap: usize = match self.sn.gap(sn) { Ok(gap) => match gap.try_into() { Ok(gap) => gap, @@ -103,7 +99,7 @@ impl ReliabilityQueue { Ok(()) } - pub(super) fn insert(&mut self, t: T, sn: ZInt) -> ZResult<()> { + pub(super) fn insert(&mut self, t: T, sn: u64) -> ZResult<()> { let gap: usize = match self.sn.gap(sn) { Ok(gap) => match gap.try_into() { Ok(gap) => gap, @@ -135,7 +131,7 @@ impl ReliabilityQueue { Ok(()) } - pub(super) fn remove(&mut self, sn: ZInt) -> ZResult { + pub(super) fn remove(&mut self, sn: u64) -> ZResult { let gap: usize = match self.sn.gap(sn) { Ok(gap) => match gap.try_into() { Ok(gap) => gap, @@ -187,8 +183,8 @@ impl ReliabilityQueue { /// Returns a bitmask of surely missed messages. /// A bit is set to 1 iff the position in the queue is empty and /// there is at least one message with a higher sequence number. - pub(super) fn get_mask(&self) -> ZInt { - let mut mask: ZInt = 0; + pub(super) fn get_mask(&self) -> u64 { + let mut mask: u64 = 0; let mut count = 0; let mut i = 0; while count < self.len() { @@ -205,7 +201,7 @@ impl ReliabilityQueue { } impl ReliabilityQueue { - pub(super) fn get(&mut self, sn: ZInt) -> ZResult { + pub(super) fn get(&mut self, sn: u64) -> ZResult { let gap: usize = match self.sn.gap(sn) { Ok(gap) => match gap.try_into() { Ok(gap) => gap, @@ -259,9 +255,9 @@ mod tests { #[test] fn reliability_queue_simple() { let size = 2; - let mut queue: ReliabilityQueue = ReliabilityQueue::new(size, 0, 2); + let mut queue: ReliabilityQueue = ReliabilityQueue::new(size, 0, 2); - let mut sn: ZInt = 0; + let mut sn: u64 = 0; // Add the first element let res = queue.insert(0, sn); assert!(res.is_ok()); @@ -282,9 +278,9 @@ mod tests { #[test] fn reliability_queue_order() { let size = 2; - let mut queue: ReliabilityQueue = ReliabilityQueue::new(size, 0, 3); + let mut queue: ReliabilityQueue = ReliabilityQueue::new(size, 0, 3); - let sn: ZInt = 0; + let sn: u64 = 0; // Add the second element let res = queue.insert(1, sn + 1); @@ -309,9 +305,9 @@ mod tests { #[test] fn reliability_queue_full() { let size = 2; - let mut queue: ReliabilityQueue = ReliabilityQueue::new(size, 0, 3); + let mut queue: ReliabilityQueue = ReliabilityQueue::new(size, 0, 3); - let mut sn: ZInt = 0; + let mut sn: u64 = 0; // Fill the queue let res = queue.insert(0, sn); @@ -336,9 +332,9 @@ mod tests { #[test] fn reliability_queue_out_of_sync() { let size = 2; - let mut queue: ReliabilityQueue = ReliabilityQueue::new(size, 0, 2); + let mut queue: ReliabilityQueue = ReliabilityQueue::new(size, 0, 2); - let sn: ZInt = 3; + let sn: u64 = 3; let res = queue.insert(sn, sn); assert!(res.is_err()); @@ -351,10 +347,10 @@ mod tests { fn reliability_queue_overflow() { // Test the overflow case let size = 4; - let mut queue: ReliabilityQueue = ReliabilityQueue::new(size, 0, 4); + let mut queue: ReliabilityQueue = ReliabilityQueue::new(size, 0, 4); - let min: ZInt = 0; - let max: ZInt = 3; + let min: u64 = 0; + let max: u64 = 3; let res = queue.set_base(max - 1); assert!(res.is_ok()); @@ -385,22 +381,22 @@ mod tests { fn reliability_queue_mask() { // Test the deterministic insertion of elements and mask let size = 8; - let mut queue: ReliabilityQueue = ReliabilityQueue::new(size, 0, 8); + let mut queue: ReliabilityQueue = ReliabilityQueue::new(size, 0, 8); - let mut sn: ZInt = 0; - while sn < size as ZInt { + let mut sn: u64 = 0; + while sn < size as u64 { let res = queue.insert(sn, sn); assert!(res.is_ok()); sn = sn + 2; } // Verify that the mask is correct - let mask: ZInt = 0b00101010; + let mask: u64 = 0b00101010; assert_eq!(queue.get_mask(), mask); // Insert the missing elements - let mut sn: ZInt = 1; - while sn < size as ZInt { + let mut sn: u64 = 1; + while sn < size as u64 { let res = queue.insert(sn, sn); assert!(res.is_ok()); sn = sn + 2; @@ -420,16 +416,16 @@ mod tests { fn reliability_queue_random_mask() { // Test the random insertion of elements and the mask let size = 64; - let mut queue: ReliabilityQueue = ReliabilityQueue::new(size, 0, 64); + let mut queue: ReliabilityQueue = ReliabilityQueue::new(size, 0, 64); - let mut sequence = Vec::::new(); - for i in 0..size as ZInt { + let mut sequence = Vec::::new(); + for i in 0..size as u64 { sequence.push(i); } let head = 0; let mut tail = 0; - let mut mask: ZInt = 0; + let mut mask: u64 = 0; let mut rng = thread_rng(); while sequence.len() > 0 { // Get random sequence number @@ -445,7 +441,7 @@ mod tests { // Locally compute the mask mask = mask | (1 << sn); let shift: u32 = tail.wrapping_sub(head) as u32; - let window = !ZInt::max_value().wrapping_shl(shift); + let window = !u64::max_value().wrapping_shl(shift); // Verify that the mask is correct assert_eq!(queue.get_mask(), !mask & window); } @@ -453,7 +449,7 @@ mod tests { // Verify that we have filled the queue assert!(queue.is_full()); // Verify that no elements are marked for retransmission - assert_eq!(queue.get_mask(), !ZInt::max_value()); + assert_eq!(queue.get_mask(), !u64::max_value()); // Drain the queue while let Some(_) = queue.pull() {} @@ -468,10 +464,10 @@ mod tests { #[test] fn reliability_queue_rebase() { let size = 8; - let mut queue: ReliabilityQueue = ReliabilityQueue::new(size, 0, 32); + let mut queue: ReliabilityQueue = ReliabilityQueue::new(size, 0, 32); // Fill the queue - for i in 0..size as ZInt { + for i in 0..size as u64 { // Push the element on the queue let res = queue.insert(i, i); assert!(res.is_ok()); @@ -523,7 +519,7 @@ mod tests { assert_eq!(queue.get_base(), 0); // Fill the queue - for i in 0..size as ZInt { + for i in 0..size as u64 { // Push the element on the queue is correct let res = queue.insert(i, i); assert!(res.is_ok()); @@ -533,7 +529,7 @@ mod tests { assert!(queue.is_full()); // Rebase beyond the current boundaries triggering a reset - let base = 2 * size as ZInt; + let base = 2 * size as u64; let res = queue.set_base(base); assert!(res.is_ok()); assert_eq!(queue.get_base(), base); @@ -549,10 +545,10 @@ mod tests { #[test] fn reliability_queue_remove() { let size = 8; - let mut queue: ReliabilityQueue = ReliabilityQueue::new(size, 0, 8); + let mut queue: ReliabilityQueue = ReliabilityQueue::new(size, 0, 8); // Fill the queue - for i in 0..size as ZInt { + for i in 0..size as u64 { // Push the element on the queue let res = queue.insert(i, i); assert!(res.is_ok()); @@ -595,7 +591,7 @@ mod tests { assert!(queue.is_empty()); // Check that everything is None - for i in 0..size as ZInt { + for i in 0..size as u64 { // Remove the element from the queue let res = queue.remove(i); assert!(res.is_err()); diff --git a/io/zenoh-transport/src/unicast/net/rx.rs b/io/zenoh-transport/src/unicast/net/rx.rs new file mode 100644 index 0000000000..2ca7406a89 --- /dev/null +++ b/io/zenoh-transport/src/unicast/net/rx.rs @@ -0,0 +1,228 @@ +use crate::transport_unicast_inner::TransportUnicastTrait; + +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use super::transport::TransportUnicastNet; +use crate::common::priority::TransportChannelRx; +use async_std::task; +use std::sync::MutexGuard; +use zenoh_buffers::{ + reader::{HasReader, Reader}, + ZSlice, +}; +use zenoh_codec::{RCodec, Zenoh080}; +use zenoh_core::{zlock, zread}; +use zenoh_link::LinkUnicast; +use zenoh_protocol::{ + core::{Priority, Reliability}, + network::NetworkMessage, + transport::{Close, Fragment, Frame, KeepAlive, TransportBody, TransportMessage, TransportSn}, +}; +use zenoh_result::{bail, zerror, ZResult}; + +/*************************************/ +/* TRANSPORT RX */ +/*************************************/ +impl TransportUnicastNet { + fn trigger_callback( + &self, + #[allow(unused_mut)] // shared-memory feature requires mut + mut msg: NetworkMessage, + ) -> ZResult<()> { + let callback = zread!(self.callback).clone(); + if let Some(callback) = callback.as_ref() { + #[cfg(feature = "shared-memory")] + { + if self.config.is_shm { + crate::shm::map_zmsg_to_shmbuf( + &mut msg, + &self.manager.state.unicast.shm.reader, + )?; + } + } + callback.handle_message(msg) + } else { + log::debug!( + "Transport: {}. No callback available, dropping message: {}", + self.config.zid, + msg + ); + Ok(()) + } + } + + fn handle_close(&self, link: &LinkUnicast, _reason: u8, session: bool) -> ZResult<()> { + // Stop now rx and tx tasks before doing the proper cleanup + let _ = self.stop_rx(link); + let _ = self.stop_tx(link); + + // Delete and clean up + let c_transport = self.clone(); + let c_link = link.clone(); + // Spawn a task to avoid a deadlock waiting for this same task + // to finish in the link close() joining the rx handle + task::spawn(async move { + if session { + let _ = c_transport.delete().await; + } else { + let _ = c_transport.del_link(&c_link).await; + } + }); + + Ok(()) + } + + fn handle_frame(&self, frame: Frame) -> ZResult<()> { + let Frame { + reliability, + sn, + ext_qos, + mut payload, + } = frame; + + let priority = ext_qos.priority(); + let c = if self.is_qos() { + &self.priority_rx[priority as usize] + } else if priority == Priority::default() { + &self.priority_rx[0] + } else { + bail!( + "Transport: {}. Unknown priority: {:?}.", + self.config.zid, + priority + ); + }; + + let mut guard = match reliability { + Reliability::Reliable => zlock!(c.reliable), + Reliability::BestEffort => zlock!(c.best_effort), + }; + + self.verify_sn(sn, &mut guard)?; + + for msg in payload.drain(..) { + self.trigger_callback(msg)?; + } + Ok(()) + } + + fn handle_fragment(&self, fragment: Fragment) -> ZResult<()> { + let Fragment { + reliability, + more, + sn, + ext_qos: qos, + payload, + } = fragment; + + let c = if self.is_qos() { + &self.priority_rx[qos.priority() as usize] + } else if qos.priority() == Priority::default() { + &self.priority_rx[0] + } else { + bail!( + "Transport: {}. Unknown priority: {:?}.", + self.config.zid, + qos.priority() + ); + }; + + let mut guard = match reliability { + Reliability::Reliable => zlock!(c.reliable), + Reliability::BestEffort => zlock!(c.best_effort), + }; + + self.verify_sn(sn, &mut guard)?; + + if guard.defrag.is_empty() { + let _ = guard.defrag.sync(sn); + } + guard.defrag.push(sn, payload)?; + if !more { + // When shared-memory feature is disabled, msg does not need to be mutable + let msg = guard + .defrag + .defragment() + .ok_or_else(|| zerror!("Transport: {}. Defragmentation error.", self.config.zid))?; + return self.trigger_callback(msg); + } + + Ok(()) + } + + fn verify_sn( + &self, + sn: TransportSn, + guard: &mut MutexGuard<'_, TransportChannelRx>, + ) -> ZResult<()> { + let precedes = guard.sn.precedes(sn)?; + if !precedes { + log::debug!( + "Transport: {}. Frame with invalid SN dropped: {}. Expected: {}.", + self.config.zid, + sn, + guard.sn.get() + ); + // Drop the fragments if needed + if !guard.defrag.is_empty() { + guard.defrag.clear(); + } + // Keep reading + return Ok(()); + } + + // Set will always return OK because we have already checked + // with precedes() that the sn has the right resolution + let _ = guard.sn.set(sn); + + Ok(()) + } + + pub(super) fn read_messages(&self, mut zslice: ZSlice, link: &LinkUnicast) -> ZResult<()> { + let codec = Zenoh080::new(); + let mut reader = zslice.reader(); + while reader.can_read() { + let msg: TransportMessage = codec + .read(&mut reader) + .map_err(|_| zerror!("{}: decoding error", link))?; + + log::trace!("Received: {:?}", msg); + + #[cfg(feature = "stats")] + { + self.stats.inc_rx_t_msgs(1); + } + + match msg.body { + TransportBody::Frame(msg) => self.handle_frame(msg)?, + TransportBody::Fragment(fragment) => self.handle_fragment(fragment)?, + TransportBody::Close(Close { reason, session }) => { + self.handle_close(link, reason, session)? + } + TransportBody::KeepAlive(KeepAlive { .. }) => {} + _ => { + log::debug!( + "Transport: {}. Message handling not implemented: {:?}", + self.config.zid, + msg + ); + } + } + } + + // Process the received message + + Ok(()) + } +} diff --git a/io/zenoh-transport/src/unicast/transport.rs b/io/zenoh-transport/src/unicast/net/transport.rs similarity index 68% rename from io/zenoh-transport/src/unicast/transport.rs rename to io/zenoh-transport/src/unicast/net/transport.rs index c2e59b75a0..95919a5f3a 100644 --- a/io/zenoh-transport/src/unicast/transport.rs +++ b/io/zenoh-transport/src/unicast/net/transport.rs @@ -11,20 +11,24 @@ // Contributors: // ZettaScale Zenoh Team, // -use super::super::{TransportExecutor, TransportManager, TransportPeerEventHandler}; -use super::common::conduit::{TransportConduitRx, TransportConduitTx}; -use super::link::TransportLinkUnicast; +use crate::common::priority::{TransportPriorityRx, TransportPriorityTx}; #[cfg(feature = "stats")] -use super::TransportUnicastStatsAtomic; +use crate::stats::TransportStats; +use crate::transport_unicast_inner::TransportUnicastTrait; +use crate::unicast::net::link::TransportLinkUnicast; +use crate::TransportConfigUnicast; +use crate::{TransportExecutor, TransportManager, TransportPeerEventHandler}; use async_std::sync::{Mutex as AsyncMutex, MutexGuard as AsyncMutexGuard}; +use async_trait::async_trait; +use std::fmt::DebugStruct; use std::sync::{Arc, RwLock}; use std::time::Duration; -use zenoh_core::{zasynclock, zread, zwrite}; +use zenoh_core::{zasynclock, zcondfeat, zread, zwrite}; use zenoh_link::{Link, LinkUnicast, LinkUnicastDirection}; +use zenoh_protocol::network::NetworkMessage; use zenoh_protocol::{ - core::{ConduitSn, Priority, WhatAmI, ZInt, ZenohId}, - transport::TransportMessage, - zenoh::ZenohMessage, + core::{Priority, WhatAmI, ZenohId}, + transport::{Close, PrioritySn, TransportMessage, TransportSn}, }; use zenoh_result::{bail, zerror, ZResult}; @@ -50,24 +54,15 @@ macro_rules! zlinkindex { /* TRANSPORT */ /*************************************/ #[derive(Clone)] -pub(crate) struct TransportUnicastConfig { +pub(crate) struct TransportUnicastNet { + // Transport Manager pub(crate) manager: TransportManager, - pub(crate) zid: ZenohId, - pub(crate) whatami: WhatAmI, - pub(crate) sn_resolution: ZInt, - pub(crate) initial_sn_tx: ZInt, - pub(crate) is_shm: bool, - pub(crate) is_qos: bool, -} - -#[derive(Clone)] -pub(crate) struct TransportUnicastInner { // Transport config - pub(super) config: TransportUnicastConfig, - // Tx conduits - pub(super) conduit_tx: Arc<[TransportConduitTx]>, - // Rx conduits - pub(super) conduit_rx: Arc<[TransportConduitRx]>, + pub(super) config: TransportConfigUnicast, + // Tx priorities + pub(super) priority_tx: Arc<[TransportPriorityTx]>, + // Rx priorities + pub(super) priority_rx: Arc<[TransportPriorityRx]>, // The links associated to the channel pub(super) links: Arc>>, // The callback @@ -76,90 +71,59 @@ pub(crate) struct TransportUnicastInner { pub(super) alive: Arc>, // Transport statistics #[cfg(feature = "stats")] - pub(super) stats: Arc, + pub(super) stats: Arc, } -impl TransportUnicastInner { - pub(super) fn make(config: TransportUnicastConfig) -> ZResult { - let mut conduit_tx = vec![]; - let mut conduit_rx = vec![]; +impl TransportUnicastNet { + pub fn make( + manager: TransportManager, + config: TransportConfigUnicast, + ) -> ZResult { + let mut priority_tx = vec![]; + let mut priority_rx = vec![]; let num = if config.is_qos { Priority::NUM } else { 1 }; for _ in 0..num { - conduit_tx.push(TransportConduitTx::make(config.sn_resolution)?); + priority_tx.push(TransportPriorityTx::make(config.sn_resolution)?); } for _ in 0..Priority::NUM { - conduit_rx.push(TransportConduitRx::make( + priority_rx.push(TransportPriorityRx::make( config.sn_resolution, - config.manager.config.defrag_buff_size, + manager.config.defrag_buff_size, )?); } - let initial_sn = ConduitSn { - reliable: config.initial_sn_tx, - best_effort: config.initial_sn_tx, + let initial_sn = PrioritySn { + reliable: config.tx_initial_sn, + best_effort: config.tx_initial_sn, }; - for c in conduit_tx.iter() { + for c in priority_tx.iter() { c.sync(initial_sn)?; } - let t = TransportUnicastInner { + let t = TransportUnicastNet { + manager, config, - conduit_tx: conduit_tx.into_boxed_slice().into(), - conduit_rx: conduit_rx.into_boxed_slice().into(), + priority_tx: priority_tx.into_boxed_slice().into(), + priority_rx: priority_rx.into_boxed_slice().into(), links: Arc::new(RwLock::new(vec![].into_boxed_slice())), callback: Arc::new(RwLock::new(None)), alive: Arc::new(AsyncMutex::new(false)), #[cfg(feature = "stats")] - stats: Arc::new(TransportUnicastStatsAtomic::default()), + stats: Arc::new(TransportStats::default()), }; Ok(t) } - pub(super) fn set_callback(&self, callback: Arc) { - let mut guard = zwrite!(self.callback); - *guard = Some(callback); - } - - pub(super) async fn get_alive(&self) -> AsyncMutexGuard<'_, bool> { - zasynclock!(self.alive) - } - - /*************************************/ - /* INITIATION */ - /*************************************/ - pub(super) async fn sync(&self, initial_sn_rx: ZInt) -> ZResult<()> { - // Mark the transport as alive and keep the lock - // to avoid concurrent new_transport and closing/closed notifications - let mut a_guard = zasynclock!(self.alive); - if *a_guard { - let e = zerror!("Transport already synched with peer: {}", self.config.zid); - log::trace!("{}", e); - return Err(e.into()); - } - - *a_guard = true; - - let csn = ConduitSn { - reliable: initial_sn_rx, - best_effort: initial_sn_rx, - }; - for c in self.conduit_rx.iter() { - c.sync(csn)?; - } - - Ok(()) - } - /*************************************/ /* TERMINATION */ /*************************************/ pub(super) async fn delete(&self) -> ZResult<()> { log::debug!( "[{}] Closing transport with peer: {}", - self.config.manager.config.zid, + self.manager.config.zid, self.config.zid ); // Mark the transport as no longer alive and keep the lock @@ -174,11 +138,7 @@ impl TransportUnicastInner { } // Delete the transport on the manager - let _ = self - .config - .manager - .del_transport_unicast(&self.config.zid) - .await; + let _ = self.manager.del_transport_unicast(&self.config.zid).await; // Close all the links let mut links = { @@ -199,96 +159,60 @@ impl TransportUnicastInner { Ok(()) } - /*************************************/ - /* LINK */ - /*************************************/ - pub(super) fn add_link( - &self, - link: LinkUnicast, - direction: LinkUnicastDirection, - ) -> ZResult<()> { - // Add the link to the channel - let mut guard = zwrite!(self.links); - - // Check if we can add more inbound links - if let LinkUnicastDirection::Inbound = direction { - let count = guard.iter().filter(|l| l.direction == direction).count(); - let limit = self.config.manager.config.unicast.max_links; - - if count >= limit { - let e = zerror!( - "Can not add Link {} with peer {}: max num of links reached {}/{}", - link, - self.config.zid, - count, - limit - ); - return Err(e.into()); - } + pub(crate) async fn del_link(&self, link: &LinkUnicast) -> ZResult<()> { + enum Target { + Transport, + Link(Box), } - // Create a channel link from a link - let link = TransportLinkUnicast::new(self.clone(), link, direction); - - let mut links = Vec::with_capacity(guard.len() + 1); - links.extend_from_slice(&guard); - links.push(link); - *guard = links.into_boxed_slice(); - - Ok(()) - } + // Try to remove the link + let target = { + let mut guard = zwrite!(self.links); - pub(super) fn start_tx( - &self, - link: &LinkUnicast, - executor: &TransportExecutor, - keep_alive: Duration, - batch_size: u16, - ) -> ZResult<()> { - let mut guard = zwrite!(self.links); - match zlinkgetmut!(guard, link) { - Some(l) => { - assert!(!self.conduit_tx.is_empty()); - l.start_tx(executor, keep_alive, batch_size, &self.conduit_tx); - Ok(()) - } - None => { + if let Some(index) = zlinkindex!(guard, link) { + let is_last = guard.len() == 1; + if is_last { + // Close the whole transport + drop(guard); + Target::Transport + } else { + // Remove the link + let mut links = guard.to_vec(); + let stl = links.remove(index); + *guard = links.into_boxed_slice(); + drop(guard); + Target::Link(stl.into()) + } + } else { bail!( - "Can not start Link TX {} with peer: {}", + "Can not delete Link {} with peer: {}", link, self.config.zid ) } + }; + + // Notify the callback + if let Some(callback) = zread!(self.callback).as_ref() { + callback.del_link(Link::from(link)); } - } - pub(super) fn stop_tx(&self, link: &LinkUnicast) -> ZResult<()> { - let mut guard = zwrite!(self.links); - match zlinkgetmut!(guard, link) { - Some(l) => { - l.stop_tx(); - Ok(()) - } - None => { - bail!( - "Can not stop Link TX {} with peer: {}", - link, - self.config.zid - ) - } + match target { + Target::Transport => self.delete().await, + Target::Link(stl) => stl.close().await, } } - pub(super) fn start_rx(&self, link: &LinkUnicast, lease: Duration) -> ZResult<()> { + pub(crate) fn stop_tx(&self, link: &LinkUnicast) -> ZResult<()> { let mut guard = zwrite!(self.links); match zlinkgetmut!(guard, link) { Some(l) => { - l.start_rx(lease); + l.stop_tx(); Ok(()) } None => { bail!( - "Can not start Link RX {} with peer: {}", + "Can not stop Link TX {} with peer: {}", link, self.config.zid ) @@ -296,7 +220,7 @@ impl TransportUnicastInner { } } - pub(super) fn stop_rx(&self, link: &LinkUnicast) -> ZResult<()> { + pub(crate) fn stop_rx(&self, link: &LinkUnicast) -> ZResult<()> { let mut guard = zwrite!(self.links); match zlinkgetmut!(guard, link) { Some(l) => { @@ -312,84 +236,125 @@ impl TransportUnicastInner { } } } +} - pub(crate) async fn del_link(&self, link: &LinkUnicast) -> ZResult<()> { - enum Target { - Transport, - Link(Box), - } +#[async_trait] +impl TransportUnicastTrait for TransportUnicastNet { + /*************************************/ + /* LINK */ + /*************************************/ + async fn add_link(&self, link: LinkUnicast, direction: LinkUnicastDirection) -> ZResult<()> { + // Add the link to the channel + let mut guard = zwrite!(self.links); - // Try to remove the link - let target = { - let mut guard = zwrite!(self.links); + // Check if we can add more inbound links + if let LinkUnicastDirection::Inbound = direction { + let count = guard.iter().filter(|l| l.direction == direction).count(); - if let Some(index) = zlinkindex!(guard, link) { - let is_last = guard.len() == 1; - if is_last { - // Close the whole transport - drop(guard); - Target::Transport - } else { - // Remove the link - let mut links = guard.to_vec(); - let stl = links.remove(index); - *guard = links.into_boxed_slice(); - drop(guard); - Target::Link(stl.into()) - } - } else { - bail!( - "Can not delete Link {} with peer: {}", + let limit = zcondfeat!( + "transport_multilink", + match self.config.multilink { + Some(_) => self.manager.config.unicast.max_links, + None => 1, + }, + 1 + ); + + if count >= limit { + let e = zerror!( + "Can not add Link {} with peer {}: max num of links reached {}/{}", link, - self.config.zid - ) + self.config.zid, + count, + limit + ); + return Err(e.into()); } - }; - - // Notify the callback - if let Some(callback) = zread!(self.callback).as_ref() { - callback.del_link(Link::from(link)); } - match target { - Target::Transport => self.delete().await, - Target::Link(stl) => stl.close().await, - } + // Create a channel link from a link + let link = TransportLinkUnicast::new(self.clone(), link, direction); + + let mut links = Vec::with_capacity(guard.len() + 1); + links.extend_from_slice(&guard); + links.push(link); + *guard = links.into_boxed_slice(); + + Ok(()) } -} -impl TransportUnicastInner { /*************************************/ /* ACCESSORS */ /*************************************/ - pub(crate) fn get_zid(&self) -> ZenohId { - self.config.zid + fn set_callback(&self, callback: Arc) { + let mut guard = zwrite!(self.callback); + *guard = Some(callback); } - pub(crate) fn get_whatami(&self) -> WhatAmI { - self.config.whatami + async fn get_alive(&self) -> AsyncMutexGuard<'_, bool> { + zasynclock!(self.alive) + } + + fn get_zid(&self) -> ZenohId { + self.config.zid } - pub(crate) fn get_sn_resolution(&self) -> ZInt { - self.config.sn_resolution + fn get_whatami(&self) -> WhatAmI { + self.config.whatami } - pub(crate) fn is_shm(&self) -> bool { + #[cfg(feature = "shared-memory")] + fn is_shm(&self) -> bool { self.config.is_shm } - pub(crate) fn is_qos(&self) -> bool { + fn is_qos(&self) -> bool { self.config.is_qos } - pub(crate) fn get_callback(&self) -> Option> { + fn get_callback(&self) -> Option> { zread!(self.callback).clone() } + fn get_config(&self) -> &TransportConfigUnicast { + &self.config + } + + #[cfg(feature = "stats")] + fn stats(&self) -> std::sync::Arc { + self.stats.clone() + } + + /*************************************/ + /* INITIATION */ + /*************************************/ + async fn sync(&self, initial_sn_rx: TransportSn) -> ZResult<()> { + // Mark the transport as alive and keep the lock + // to avoid concurrent new_transport and closing/closed notifications + let mut a_guard = zasynclock!(self.alive); + if *a_guard { + let e = zerror!("Transport already synched with peer: {}", self.config.zid); + log::trace!("{}", e); + return Err(e.into()); + } + + *a_guard = true; + + let csn = PrioritySn { + reliable: initial_sn_rx, + best_effort: initial_sn_rx, + }; + for c in self.priority_rx.iter() { + c.sync(csn)?; + } + + Ok(()) + } + /*************************************/ /* TERMINATION */ /*************************************/ - pub(crate) async fn close_link(&self, link: &LinkUnicast, reason: u8) -> ZResult<()> { + async fn close_link(&self, link: &LinkUnicast, reason: u8) -> ZResult<()> { log::trace!("Closing link {} with peer: {}", link, self.config.zid); let mut pipeline = zlinkget!(zread!(self.links), link) @@ -398,11 +363,11 @@ impl TransportUnicastInner { if let Some(p) = pipeline.take() { // Close message to be sent on the target link - let peer_id = Some(self.config.manager.zid()); - let reason_id = reason; - let link_only = true; // This is should always be true when closing a link - let attachment = None; // No attachment here - let msg = TransportMessage::make_close(peer_id, reason_id, link_only, attachment); + let msg: TransportMessage = Close { + reason, + session: false, + } + .into(); p.push_transport_message(msg, Priority::Background); } @@ -411,7 +376,7 @@ impl TransportUnicastInner { self.del_link(link).await } - pub(crate) async fn close(&self, reason: u8) -> ZResult<()> { + async fn close(&self, reason: u8) -> ZResult<()> { log::trace!("Closing transport with peer: {}", self.config.zid); let mut pipelines = zread!(self.links) @@ -420,14 +385,14 @@ impl TransportUnicastInner { .collect::>(); for p in pipelines.drain(..) { // Close message to be sent on all the links - let peer_id = Some(self.config.manager.zid()); - let reason_id = reason; - // link_only should always be false for user-triggered close. However, in case of + // session should always be true for user-triggered close. However, in case of // multiple links, it is safer to close all the links first. When no links are left, // the transport is then considered closed. - let link_only = true; - let attachment = None; // No attachment here - let msg = TransportMessage::make_close(peer_id, reason_id, link_only, attachment); + let msg: TransportMessage = Close { + reason, + session: false, + } + .into(); p.push_transport_message(msg, Priority::Background); } @@ -435,28 +400,65 @@ impl TransportUnicastInner { self.delete().await } + fn get_links(&self) -> Vec { + zread!(self.links).iter().map(|l| l.link.clone()).collect() + } + /*************************************/ - /* SCHEDULE AND SEND TX */ + /* TX */ /*************************************/ - /// Schedule a Zenoh message on the transmission queue - pub(crate) fn schedule(&self, #[allow(unused_mut)] mut message: ZenohMessage) -> bool { - #[cfg(feature = "shared-memory")] - { - let res = if self.config.is_shm { - crate::shm::map_zmsg_to_shminfo(&mut message) - } else { - crate::shm::map_zmsg_to_shmbuf(&mut message, &self.config.manager.shmr) - }; - if let Err(e) = res { - log::trace!("Failed SHM conversion: {}", e); - return false; + fn schedule(&self, msg: NetworkMessage) -> ZResult<()> { + match self.internal_schedule(msg) { + true => Ok(()), + false => bail!("error scheduling message!"), + } + } + + fn start_tx( + &self, + link: &LinkUnicast, + executor: &TransportExecutor, + keep_alive: Duration, + batch_size: u16, + ) -> ZResult<()> { + let mut guard = zwrite!(self.links); + match zlinkgetmut!(guard, link) { + Some(l) => { + assert!(!self.priority_tx.is_empty()); + l.start_tx(executor, keep_alive, batch_size, &self.priority_tx); + Ok(()) + } + None => { + bail!( + "Can not start Link TX {} with peer: {}", + link, + self.config.zid + ) } } + } - self.schedule_first_fit(message) + fn start_rx(&self, link: &LinkUnicast, lease: Duration, batch_size: u16) -> ZResult<()> { + let mut guard = zwrite!(self.links); + match zlinkgetmut!(guard, link) { + Some(l) => { + l.start_rx(lease, batch_size); + Ok(()) + } + None => { + bail!( + "Can not start Link RX {} with peer: {}", + link, + self.config.zid + ) + } + } } - pub(crate) fn get_links(&self) -> Vec { - zread!(self.links).iter().map(|l| l.link.clone()).collect() + fn add_debug_fields<'a, 'b: 'a, 'c>( + &self, + s: &'c mut DebugStruct<'a, 'b>, + ) -> &'c mut DebugStruct<'a, 'b> { + s.field("sn_resolution", &self.config.sn_resolution) } } diff --git a/io/zenoh-transport/src/unicast/tx.rs b/io/zenoh-transport/src/unicast/net/tx.rs similarity index 56% rename from io/zenoh-transport/src/unicast/tx.rs rename to io/zenoh-transport/src/unicast/net/tx.rs index c831e99704..36e67f1f18 100644 --- a/io/zenoh-transport/src/unicast/tx.rs +++ b/io/zenoh-transport/src/unicast/net/tx.rs @@ -11,16 +11,12 @@ // Contributors: // ZettaScale Zenoh Team, // -use super::transport::TransportUnicastInner; -#[cfg(feature = "stats")] -use zenoh_buffers::SplitBuffer; +use super::transport::TransportUnicastNet; use zenoh_core::zread; -#[cfg(feature = "stats")] -use zenoh_protocol::zenoh::ZenohBody; -use zenoh_protocol::zenoh::ZenohMessage; +use zenoh_protocol::network::NetworkMessage; -impl TransportUnicastInner { - fn schedule_on_link(&self, msg: ZenohMessage) -> bool { +impl TransportUnicastNet { + fn schedule_on_link(&self, msg: NetworkMessage) -> bool { macro_rules! zpush { ($guard:expr, $pipeline:expr, $msg:expr) => { // Drop the guard before the push_zenoh_message since @@ -29,7 +25,7 @@ impl TransportUnicastInner { let pl = $pipeline.clone(); drop($guard); log::trace!("Scheduled: {:?}", $msg); - return pl.push_zenoh_message($msg); + return pl.push_network_message($msg); }; } @@ -63,39 +59,30 @@ impl TransportUnicastInner { false } + #[allow(unused_mut)] // When feature "shared-memory" is not enabled #[allow(clippy::let_and_return)] // When feature "stats" is not enabled #[inline(always)] - pub(super) fn schedule_first_fit(&self, msg: ZenohMessage) -> bool { - #[cfg(feature = "stats")] - match &msg.body { - ZenohBody::Data(data) => match data.reply_context { - Some(_) => { - self.stats.inc_tx_z_data_reply_msgs(1); - self.stats - .inc_tx_z_data_reply_payload_bytes(data.payload.len()); - } - None => { - self.stats.inc_tx_z_data_msgs(1); - self.stats.inc_tx_z_data_payload_bytes(data.payload.len()); - } - }, - ZenohBody::Unit(unit) => match unit.reply_context { - Some(_) => self.stats.inc_tx_z_unit_reply_msgs(1), - None => self.stats.inc_tx_z_unit_msgs(1), - }, - ZenohBody::Pull(_) => self.stats.inc_tx_z_pull_msgs(1), - ZenohBody::Query(_) => self.stats.inc_tx_z_query_msgs(1), - ZenohBody::Declare(_) => self.stats.inc_tx_z_declare_msgs(1), - ZenohBody::LinkStateList(_) => self.stats.inc_tx_z_linkstate_msgs(1), + pub(crate) fn internal_schedule(&self, mut msg: NetworkMessage) -> bool { + #[cfg(feature = "shared-memory")] + { + let res = if self.config.is_shm { + crate::shm::map_zmsg_to_shminfo(&mut msg) + } else { + crate::shm::map_zmsg_to_shmbuf(&mut msg, &self.manager.shm().reader) + }; + if let Err(e) = res { + log::trace!("Failed SHM conversion: {}", e); + return false; + } } let res = self.schedule_on_link(msg); #[cfg(feature = "stats")] if res { - self.stats.inc_tx_z_msgs(1); + self.stats.inc_tx_n_msgs(1); } else { - self.stats.inc_tx_z_dropped(1); + self.stats.inc_tx_n_dropped(1); } res diff --git a/io/zenoh-transport/src/unicast/rx.rs b/io/zenoh-transport/src/unicast/rx.rs deleted file mode 100644 index 0727f5ed2e..0000000000 --- a/io/zenoh-transport/src/unicast/rx.rs +++ /dev/null @@ -1,216 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use super::common::conduit::TransportChannelRx; -use super::transport::TransportUnicastInner; -use async_std::task; -use std::sync::MutexGuard; -#[cfg(feature = "stats")] -use zenoh_buffers::SplitBuffer; -use zenoh_core::{zlock, zread}; -use zenoh_link::LinkUnicast; -#[cfg(feature = "stats")] -use zenoh_protocol::zenoh::ZenohBody; -use zenoh_protocol::{ - core::{Priority, Reliability, ZInt, ZenohId}, - transport::{tmsg, Close, Frame, FramePayload, KeepAlive, TransportBody, TransportMessage}, - zenoh::ZenohMessage, -}; -use zenoh_result::{bail, zerror, ZResult}; - -/*************************************/ -/* TRANSPORT RX */ -/*************************************/ -impl TransportUnicastInner { - fn trigger_callback( - &self, - #[allow(unused_mut)] // shared-memory feature requires mut - mut msg: ZenohMessage, - ) -> ZResult<()> { - #[cfg(feature = "stats")] - { - self.stats.inc_rx_z_msgs(1); - match &msg.body { - ZenohBody::Data(data) => match data.reply_context { - Some(_) => { - self.stats.inc_rx_z_data_reply_msgs(1); - self.stats - .inc_rx_z_data_reply_payload_bytes(data.payload.len()); - } - None => { - self.stats.inc_rx_z_data_msgs(1); - self.stats.inc_rx_z_data_payload_bytes(data.payload.len()); - } - }, - ZenohBody::Unit(unit) => match unit.reply_context { - Some(_) => self.stats.inc_rx_z_unit_reply_msgs(1), - None => self.stats.inc_rx_z_unit_msgs(1), - }, - ZenohBody::Pull(_) => self.stats.inc_rx_z_pull_msgs(1), - ZenohBody::Query(_) => self.stats.inc_rx_z_query_msgs(1), - ZenohBody::Declare(_) => self.stats.inc_rx_z_declare_msgs(1), - ZenohBody::LinkStateList(_) => self.stats.inc_rx_z_linkstate_msgs(1), - } - } - - let callback = zread!(self.callback).clone(); - if let Some(callback) = callback.as_ref() { - #[cfg(feature = "shared-memory")] - { - crate::shm::map_zmsg_to_shmbuf(&mut msg, &self.config.manager.shmr)?; - } - callback.handle_message(msg) - } else { - log::debug!( - "Transport: {}. No callback available, dropping message: {}", - self.config.zid, - msg - ); - Ok(()) - } - } - - fn handle_close( - &self, - link: &LinkUnicast, - zid: Option, - reason: u8, - link_only: bool, - ) -> ZResult<()> { - // Check if the PID is correct when provided - if let Some(zid) = zid { - if zid != self.config.zid { - bail!( - "Transport: {}. Invalid close zid: {} reason: {}.", - self.config.zid, - zid, - tmsg::close_reason_to_str(reason) - ); - } - } - - // Stop now rx and tx tasks before doing the proper cleanup - let _ = self.stop_rx(link); - let _ = self.stop_tx(link); - - // Delete and clean up - let c_transport = self.clone(); - let c_link = link.clone(); - // Spawn a task to avoid a deadlock waiting for this same task - // to finish in the link close() joining the rx handle - task::spawn(async move { - if link_only { - let _ = c_transport.del_link(&c_link).await; - } else { - let _ = c_transport.delete().await; - } - }); - - Ok(()) - } - - fn handle_frame( - &self, - sn: ZInt, - payload: FramePayload, - mut guard: MutexGuard<'_, TransportChannelRx>, - ) -> ZResult<()> { - let precedes = guard.sn.precedes(sn)?; - if !precedes { - log::debug!( - "Transport: {}. Frame with invalid SN dropped: {}. Expected: {}.", - self.config.zid, - sn, - guard.sn.get() - ); - // Drop the fragments if needed - if !guard.defrag.is_empty() { - guard.defrag.clear(); - } - // Keep reading - return Ok(()); - } - - // Set will always return OK because we have already checked - // with precedes() that the sn has the right resolution - let _ = guard.sn.set(sn); - match payload { - FramePayload::Fragment { buffer, is_final } => { - if guard.defrag.is_empty() { - let _ = guard.defrag.sync(sn); - } - guard.defrag.push(sn, buffer)?; - if is_final { - // When shared-memory feature is disabled, msg does not need to be mutable - let msg = guard.defrag.defragment().ok_or_else(|| { - zerror!("Transport: {}. Defragmentation error.", self.config.zid) - })?; - self.trigger_callback(msg) - } else { - Ok(()) - } - } - FramePayload::Messages { mut messages } => { - for msg in messages.drain(..) { - self.trigger_callback(msg)?; - } - Ok(()) - } - } - } - - pub(super) fn receive_message(&self, msg: TransportMessage, link: &LinkUnicast) -> ZResult<()> { - log::trace!("Received: {:?}", msg); - // Process the received message - match msg.body { - TransportBody::Frame(Frame { - channel, - sn, - payload, - }) => { - let c = if self.is_qos() { - &self.conduit_rx[channel.priority as usize] - } else if channel.priority == Priority::default() { - &self.conduit_rx[0] - } else { - bail!( - "Transport: {}. Unknown conduit: {:?}.", - self.config.zid, - channel.priority - ); - }; - - match channel.reliability { - Reliability::Reliable => self.handle_frame(sn, payload, zlock!(c.reliable)), - Reliability::BestEffort => { - self.handle_frame(sn, payload, zlock!(c.best_effort)) - } - } - } - TransportBody::Close(Close { - zid, - reason, - link_only, - }) => self.handle_close(link, zid, reason, link_only), - TransportBody::KeepAlive(KeepAlive { .. }) => Ok(()), - _ => { - log::debug!( - "Transport: {}. Message handling not implemented: {:?}", - self.config.zid, - msg - ); - Ok(()) - } - } - } -} diff --git a/io/zenoh-transport/src/unicast/shared_memory_unicast.rs b/io/zenoh-transport/src/unicast/shared_memory_unicast.rs new file mode 100644 index 0000000000..ce940444af --- /dev/null +++ b/io/zenoh-transport/src/unicast/shared_memory_unicast.rs @@ -0,0 +1,57 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use async_std::sync::RwLock; +use rand::{Rng, SeedableRng}; +use zenoh_core::zerror; +use zenoh_crypto::PseudoRng; +use zenoh_result::ZResult; +use zenoh_shm::{SharedMemoryBuf, SharedMemoryManager, SharedMemoryReader}; + +pub(crate) type Challenge = u64; +const NAME: &str = "zshm"; + +/*************************************/ +/* Authenticator */ +/*************************************/ +pub(crate) struct SharedMemoryUnicast { + // Rust guarantees that fields are dropped in the order of declaration. + // Buffer needs to be dropped before the manager. + pub(crate) challenge: SharedMemoryBuf, + pub(crate) _manager: SharedMemoryManager, + pub(crate) reader: RwLock, +} + +unsafe impl Sync for SharedMemoryUnicast {} + +impl SharedMemoryUnicast { + pub fn make() -> ZResult { + // Create a challenge for session establishment + let mut prng = PseudoRng::from_entropy(); + let nonce = prng.gen::(); + let size = std::mem::size_of::(); + + let mut _manager = SharedMemoryManager::make(format!("{NAME}.{nonce}"), size)?; + + let mut challenge = _manager.alloc(size).map_err(|e| zerror!("{e}"))?; + let slice = unsafe { challenge.as_mut_slice() }; + slice[0..size].copy_from_slice(&nonce.to_le_bytes()); + + let shmauth = SharedMemoryUnicast { + challenge, + _manager, + reader: RwLock::new(SharedMemoryReader::new()), + }; + Ok(shmauth) + } +} diff --git a/io/zenoh-transport/src/unicast/shm/link.rs b/io/zenoh-transport/src/unicast/shm/link.rs new file mode 100644 index 0000000000..5b5c9b764b --- /dev/null +++ b/io/zenoh-transport/src/unicast/shm/link.rs @@ -0,0 +1,273 @@ +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use super::transport::TransportUnicastShm; +use crate::TransportExecutor; +use async_std::task; +use async_std::{prelude::FutureExt, sync::RwLock}; +use zenoh_codec::*; +use zenoh_core::{zasyncread, zasyncwrite}; + +use std::sync::Arc; +use std::time::Duration; +use zenoh_buffers::{writer::HasWriter, ZSlice}; +use zenoh_link::LinkUnicast; +use zenoh_protocol::transport::{BatchSize, KeepAlive, TransportBodyShm, TransportMessageShm}; +use zenoh_result::{zerror, ZResult}; +use zenoh_sync::RecyclingObjectPool; + +pub(crate) async fn send_with_link(link: &LinkUnicast, msg: TransportMessageShm) -> ZResult<()> { + if link.is_streamed() { + let mut buffer = vec![0, 0, 0, 0]; + let codec = Zenoh080::new(); + let mut writer = buffer.writer(); + codec + .write(&mut writer, &msg) + .map_err(|_| zerror!("Error serializing message {:?}", msg))?; + + let len = (buffer.len() - 4) as u32; + let le = len.to_le_bytes(); + + buffer[0..4].copy_from_slice(&le); + + link.write_all(&buffer).await?; + } else { + let mut buffer = vec![]; + let codec = Zenoh080::new(); + let mut writer = buffer.writer(); + codec + .write(&mut writer, &msg) + .map_err(|_| zerror!("Error serializing message {:?}", msg))?; + + link.write_all(&buffer).await?; + } + log::trace!("Sent: {:?}", msg); + + // #[cfg(feature = "stats")] + // { + // stats.inc_tx_t_msgs(1); + // stats.inc_tx_bytes(buff.len() + 2); + // } + + Ok(()) +} + +impl TransportUnicastShm { + pub(super) fn send(&self, msg: TransportMessageShm) -> ZResult<()> { + async_std::task::block_on(self.send_async(msg)) + } + + pub(super) async fn send_async(&self, msg: TransportMessageShm) -> ZResult<()> { + let guard = zasyncwrite!(self.link); + send_with_link(&guard, msg).await + } + + pub(super) fn start_keepalive(&self, executor: &TransportExecutor, keep_alive: Duration) { + let mut guard = async_std::task::block_on(async { zasyncwrite!(self.handle_keepalive) }); + let c_transport = self.clone(); + let handle = executor.spawn(async move { + let res = keepalive_task( + c_transport.link.clone(), + keep_alive, + // #[cfg(feature = "stats")] + // c_transport.stats, + ) + .await; + log::debug!( + "[{}] Keepalive task finished with result {:?}", + c_transport.manager.config.zid, + res + ); + if res.is_err() { + log::debug!( + "[{}] finalizing transport with peer: {}", + c_transport.manager.config.zid, + c_transport.config.zid + ); + let _ = c_transport.finalize(0).await; + } + }); + *guard = Some(handle); + } + + pub(super) async fn stop_keepalive(&self) { + let zid = self.manager.config.zid; + log::debug!("[{}] Stopping keepalive task...", zid,); + let mut guard = zasyncwrite!(self.handle_keepalive); + let handle = guard.take(); + drop(guard); + + if let Some(handle) = handle { + let _ = handle.cancel().await; + log::debug!("[{}] keepalive task stopped...", zid,); + } + } + + pub(super) fn internal_start_rx(&self, lease: Duration, batch_size: u16) { + let mut guard = async_std::task::block_on(async { zasyncwrite!(self.handle_rx) }); + let c_transport = self.clone(); + let handle = task::spawn(async move { + let guard = zasyncread!(c_transport.link); + let link = guard.clone(); + drop(guard); + let rx_buffer_size = c_transport.manager.config.link_rx_buffer_size; + + // Start the rx task + let res = rx_task(link, c_transport.clone(), lease, batch_size, rx_buffer_size).await; + log::debug!( + "[{}] Rx task finished with result {:?}", + c_transport.manager.config.zid, + res + ); + if res.is_err() { + log::debug!( + "[{}] finalizing transport with peer: {}", + c_transport.manager.config.zid, + c_transport.config.zid + ); + let _ = c_transport.finalize(0).await; + } + }); + *guard = Some(handle); + } + + pub(super) async fn stop_rx(&self) { + let zid = self.manager.config.zid; + log::debug!("[{}] Stopping rx task...", zid,); + let mut guard = zasyncwrite!(self.handle_rx); + let handle = guard.take(); + drop(guard); + + if let Some(handle) = handle { + let _ = handle.cancel().await; + log::debug!("[{}] rx task stopped...", zid,); + } + } +} + +/*************************************/ +/* TASKS */ +/*************************************/ +async fn keepalive_task( + link: Arc>, + keep_alive: Duration, + // #[cfg(feature = "stats")] stats: Arc, +) -> ZResult<()> { + loop { + async_std::task::sleep(keep_alive).await; + + let keepailve = TransportMessageShm { + body: TransportBodyShm::KeepAlive(KeepAlive), + }; + + let guard = zasyncwrite!(link); + let _ = send_with_link( + &guard, keepailve, + // #[cfg(feature = "stats")] + // &stats, + ) + .await; + drop(guard); + } +} + +async fn rx_task_stream( + link: LinkUnicast, + transport: TransportUnicastShm, + lease: Duration, + rx_batch_size: BatchSize, + rx_buffer_size: usize, +) -> ZResult<()> { + async fn read(link: &LinkUnicast, buffer: &mut [u8]) -> ZResult { + // 16 bits for reading the batch length + let mut length = [0_u8, 0_u8, 0_u8, 0_u8]; + link.read_exact(&mut length).await?; + let n = u32::from_le_bytes(length) as usize; + + link.read_exact(&mut buffer[0..n]).await?; + Ok(n) + } + + // The pool of buffers + let mtu = link.get_mtu().min(rx_batch_size) as usize; + let mut n = rx_buffer_size / mtu; + if rx_buffer_size % mtu != 0 { + n += 1; + } + + let pool = RecyclingObjectPool::new(n, || vec![0_u8; mtu].into_boxed_slice()); + loop { + // Retrieve one buffer + let mut buffer = pool.try_take().unwrap_or_else(|| pool.alloc()); + + // Async read from the underlying link + let bytes = read(&link, &mut buffer) + .timeout(lease) + .await + .map_err(|_| zerror!("{}: expired after {} milliseconds", link, lease.as_millis()))??; + #[cfg(feature = "stats")] + transport.stats.inc_rx_bytes(2 + bytes); // Account for the batch len encoding (16 bits) + + // Deserialize all the messages from the current ZBuf + let zslice = ZSlice::make(Arc::new(buffer), 0, bytes).unwrap(); + transport.read_messages(zslice, &link).await?; + } +} + +async fn rx_task_dgram( + link: LinkUnicast, + transport: TransportUnicastShm, + lease: Duration, + rx_batch_size: BatchSize, + rx_buffer_size: usize, +) -> ZResult<()> { + // The pool of buffers + let mtu = link.get_mtu().min(rx_batch_size) as usize; + let mut n = rx_buffer_size / mtu; + if rx_buffer_size % mtu != 0 { + n += 1; + } + + let pool = RecyclingObjectPool::new(n, || vec![0_u8; mtu].into_boxed_slice()); + loop { + // Retrieve one buffer + let mut buffer = pool.try_take().unwrap_or_else(|| pool.alloc()); + + // Async read from the underlying link + let bytes = + link.read(&mut buffer).timeout(lease).await.map_err(|_| { + zerror!("{}: expired after {} milliseconds", link, lease.as_millis()) + })??; + + #[cfg(feature = "stats")] + transport.stats.inc_rx_bytes(bytes); + + // Deserialize all the messages from the current ZBuf + let zslice = ZSlice::make(Arc::new(buffer), 0, bytes).unwrap(); + transport.read_messages(zslice, &link).await?; + } +} + +async fn rx_task( + link: LinkUnicast, + transport: TransportUnicastShm, + lease: Duration, + rx_batch_size: u16, + rx_buffer_size: usize, +) -> ZResult<()> { + if link.is_streamed() { + rx_task_stream(link, transport, lease, rx_batch_size, rx_buffer_size).await + } else { + rx_task_dgram(link, transport, lease, rx_batch_size, rx_buffer_size).await + } +} diff --git a/io/zenoh-transport/src/unicast/shm/mod.rs b/io/zenoh-transport/src/unicast/shm/mod.rs new file mode 100644 index 0000000000..7cb4274baa --- /dev/null +++ b/io/zenoh-transport/src/unicast/shm/mod.rs @@ -0,0 +1,18 @@ +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +pub(crate) mod transport; + +mod link; +mod rx; +mod tx; diff --git a/io/zenoh-transport/src/unicast/shm/rx.rs b/io/zenoh-transport/src/unicast/shm/rx.rs new file mode 100644 index 0000000000..75ddd31586 --- /dev/null +++ b/io/zenoh-transport/src/unicast/shm/rx.rs @@ -0,0 +1,84 @@ +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use super::transport::TransportUnicastShm; +use zenoh_buffers::{ + reader::{HasReader, Reader}, + ZSlice, +}; +use zenoh_codec::{RCodec, Zenoh080}; +use zenoh_core::zread; +use zenoh_link::LinkUnicast; +use zenoh_protocol::{network::NetworkMessage, transport::TransportMessageShm}; +use zenoh_result::{zerror, ZResult}; + +/*************************************/ +/* TRANSPORT RX */ +/*************************************/ +impl TransportUnicastShm { + fn trigger_callback( + &self, + #[allow(unused_mut)] // shared-memory feature requires mut + mut msg: NetworkMessage, + ) -> ZResult<()> { + let callback = zread!(self.callback).clone(); + if let Some(callback) = callback.as_ref() { + #[cfg(feature = "shared-memory")] + { + if self.config.is_shm { + crate::shm::map_zmsg_to_shmbuf(&mut msg, &self.manager.shm().reader)?; + } + } + callback.handle_message(msg) + } else { + log::debug!( + "Transport: {}. No callback available, dropping message: {}", + self.config.zid, + msg + ); + Ok(()) + } + } + + pub(super) async fn read_messages( + &self, + mut zslice: ZSlice, + link: &LinkUnicast, + ) -> ZResult<()> { + let codec = Zenoh080::new(); + let mut reader = zslice.reader(); + while reader.can_read() { + let msg: TransportMessageShm = codec + .read(&mut reader) + .map_err(|_| zerror!("{}: decoding error", link))?; + + log::trace!("Received: {:?}", msg); + + // #[cfg(feature = "stats")] + // { + // transport.stats.inc_rx_t_msgs(1); + // } + + match msg.body { + zenoh_protocol::transport::TransportBodyShm::Close(_) => { + let _ = self.delete().await; + } + zenoh_protocol::transport::TransportBodyShm::KeepAlive(_) => {} + zenoh_protocol::transport::TransportBodyShm::Network(msg) => { + let _ = self.trigger_callback(*msg); + } + } + } + Ok(()) + } +} diff --git a/io/zenoh-transport/src/unicast/shm/transport.rs b/io/zenoh-transport/src/unicast/shm/transport.rs new file mode 100644 index 0000000000..f5fe683041 --- /dev/null +++ b/io/zenoh-transport/src/unicast/shm/transport.rs @@ -0,0 +1,311 @@ +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +#[cfg(feature = "transport_shm")] +use super::link::send_with_link; +#[cfg(feature = "stats")] +use crate::stats::TransportStats; +use crate::transport_unicast_inner::TransportUnicastTrait; +use crate::TransportConfigUnicast; +use crate::TransportManager; +use crate::{TransportExecutor, TransportPeerEventHandler}; +use async_executor::Task; +#[cfg(feature = "transport_shm")] +use async_std::sync::RwLockUpgradableReadGuard; +use async_std::sync::{Mutex as AsyncMutex, MutexGuard as AsyncMutexGuard, RwLock}; +use async_std::task::JoinHandle; +use async_trait::async_trait; +use std::sync::{Arc, RwLock as SyncRwLock}; +use std::time::Duration; +#[cfg(feature = "transport_shm")] +use zenoh_core::zasyncread_upgradable; +use zenoh_core::{zasynclock, zasyncread, zread, zwrite}; +#[cfg(feature = "transport_shm")] +use zenoh_link::Link; +use zenoh_link::{LinkUnicast, LinkUnicastDirection}; +use zenoh_protocol::core::{WhatAmI, ZenohId}; +use zenoh_protocol::network::NetworkMessage; +use zenoh_protocol::transport::TransportBodyShm; +use zenoh_protocol::transport::TransportMessageShm; +use zenoh_protocol::transport::{Close, TransportSn}; +#[cfg(not(feature = "transport_shm"))] +use zenoh_result::bail; +use zenoh_result::{zerror, ZResult}; + +/*************************************/ +/* TRANSPORT */ +/*************************************/ +#[derive(Clone)] +pub(crate) struct TransportUnicastShm { + // Transport Manager + pub(super) manager: TransportManager, + // Transport config + pub(super) config: TransportConfigUnicast, + // The link associated to the transport + pub(super) link: Arc>, + // The callback + pub(super) callback: Arc>>>, + // Mutex for notification + alive: Arc>, + // Transport statistics + #[cfg(feature = "stats")] + pub(super) stats: Arc, + + // The flags to stop TX/RX tasks + pub(crate) handle_keepalive: Arc>>>, + pub(crate) handle_rx: Arc>>>, +} + +impl TransportUnicastShm { + pub fn make( + manager: TransportManager, + config: TransportConfigUnicast, + link: LinkUnicast, + ) -> ZResult { + let t = TransportUnicastShm { + manager, + config, + link: Arc::new(RwLock::new(link)), + callback: Arc::new(SyncRwLock::new(None)), + alive: Arc::new(AsyncMutex::new(false)), + #[cfg(feature = "stats")] + stats: Arc::new(TransportStats::default()), + handle_keepalive: Arc::new(RwLock::new(None)), + handle_rx: Arc::new(RwLock::new(None)), + }; + + Ok(t) + } + + /*************************************/ + /* TERMINATION */ + /*************************************/ + pub(super) async fn finalize(&self, reason: u8) -> ZResult<()> { + log::debug!( + "[{}] Finalizing transport with peer: {}", + self.manager.config.zid, + self.config.zid + ); + + // Send close message on the link + let close = TransportMessageShm { + body: TransportBodyShm::Close(Close { + reason, + session: false, + }), + }; + let _ = self.send_async(close).await; + + // Terminate and clean up the transport + self.delete().await + } + + pub(super) async fn delete(&self) -> ZResult<()> { + log::debug!( + "[{}] Deleting transport with peer: {}", + self.manager.config.zid, + self.config.zid + ); + // Mark the transport as no longer alive and keep the lock + // to avoid concurrent new_transport and closing/closed notifications + let mut a_guard = self.get_alive().await; + *a_guard = false; + + // Notify the callback that we are going to close the transport + let callback = zwrite!(self.callback).take(); + if let Some(cb) = callback.as_ref() { + cb.closing(); + } + + // Delete the transport on the manager + let _ = self.manager.del_transport_unicast(&self.config.zid).await; + + // Close and drop the link + self.stop_keepalive().await; + self.stop_rx().await; + let _ = zasyncread!(self.link).close().await; + + // Notify the callback that we have closed the transport + if let Some(cb) = callback.as_ref() { + cb.closed(); + } + + Ok(()) + } +} + +#[async_trait] +impl TransportUnicastTrait for TransportUnicastShm { + /*************************************/ + /* ACCESSORS */ + /*************************************/ + fn set_callback(&self, callback: Arc) { + let mut guard = zwrite!(self.callback); + *guard = Some(callback); + } + + async fn get_alive(&self) -> AsyncMutexGuard<'_, bool> { + zasynclock!(self.alive) + } + + fn get_links(&self) -> Vec { + let guard = async_std::task::block_on(async { zasyncread!(self.link) }); + [guard.clone()].to_vec() + } + + fn get_zid(&self) -> ZenohId { + self.config.zid + } + + fn get_whatami(&self) -> WhatAmI { + self.config.whatami + } + + #[cfg(feature = "shared-memory")] + fn is_shm(&self) -> bool { + self.config.is_shm + } + + fn is_qos(&self) -> bool { + self.config.is_qos + } + + fn get_callback(&self) -> Option> { + zread!(self.callback).clone() + } + + fn get_config(&self) -> &TransportConfigUnicast { + &self.config + } + + #[cfg(feature = "stats")] + fn stats(&self) -> std::sync::Arc { + self.stats.clone() + } + + /*************************************/ + /* TX */ + /*************************************/ + fn schedule(&self, msg: NetworkMessage) -> ZResult<()> { + self.internal_schedule(msg) + } + + fn start_tx( + &self, + _link: &LinkUnicast, + executor: &TransportExecutor, + keep_alive: Duration, + _batch_size: u16, + ) -> ZResult<()> { + self.start_keepalive(executor, keep_alive); + Ok(()) + } + + fn start_rx(&self, _link: &LinkUnicast, lease: Duration, batch_size: u16) -> ZResult<()> { + self.internal_start_rx(lease, batch_size); + Ok(()) + } + + /*************************************/ + /* LINK */ + /*************************************/ + async fn add_link(&self, link: LinkUnicast, _direction: LinkUnicastDirection) -> ZResult<()> { + log::trace!("Adding link: {}", link); + + #[cfg(not(feature = "transport_shm"))] + bail!( + "Can not add Link {} with peer {}: link already exists and only unique link is supported!", + link, + self.config.zid, + ); + + #[cfg(feature = "transport_shm")] + { + let guard = zasyncread_upgradable!(self.link); + + let shm_protocol = "shm"; + let existing_shm = guard.get_dst().protocol().as_str() == shm_protocol; + let new_shm = link.get_dst().protocol().as_str() == shm_protocol; + match (existing_shm, new_shm) { + (false, true) => { + // SHM transport suports only a single link, but code here also handles upgrade from non-shm link to shm link! + log::trace!( + "Upgrading {} SHM transport's link from {} to {}", + self.config.zid, + guard, + link + ); + + // Prepare and send close message on old link + { + let close = TransportMessageShm { + body: TransportBodyShm::Close(Close { + reason: 0, + session: false, + }), + }; + let _ = send_with_link(&guard, close).await; + }; + // Notify the callback + if let Some(callback) = zread!(self.callback).as_ref() { + callback.del_link(Link::from(guard.clone())); + } + + // Set the new link + let mut write_guard = RwLockUpgradableReadGuard::upgrade(guard).await; + *write_guard = link; + + Ok(()) + } + _ => { + let e = zerror!( + "Can not add Link {} with peer {}: link already exists and only unique link is supported!", + link, + self.config.zid, + ); + Err(e.into()) + } + } + } + } + + /*************************************/ + /* INITIATION */ + /*************************************/ + async fn sync(&self, _initial_sn_rx: TransportSn) -> ZResult<()> { + // Mark the transport as alive + let mut a_guard = zasynclock!(self.alive); + if *a_guard { + let e = zerror!("Transport already synched with peer: {}", self.config.zid); + log::trace!("{}", e); + return Err(e.into()); + } + + *a_guard = true; + + Ok(()) + } + + /*************************************/ + /* TERMINATION */ + /*************************************/ + async fn close_link(&self, link: &LinkUnicast, reason: u8) -> ZResult<()> { + log::trace!("Closing link {} with peer: {}", link, self.config.zid); + self.finalize(reason).await + } + + async fn close(&self, reason: u8) -> ZResult<()> { + log::trace!("Closing transport with peer: {}", self.config.zid); + self.finalize(reason).await + } +} diff --git a/io/zenoh-transport/src/unicast/shm/tx.rs b/io/zenoh-transport/src/unicast/shm/tx.rs new file mode 100644 index 0000000000..b93acc65fd --- /dev/null +++ b/io/zenoh-transport/src/unicast/shm/tx.rs @@ -0,0 +1,52 @@ +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use super::transport::TransportUnicastShm; +use zenoh_protocol::{ + network::NetworkMessage, + transport::{TransportBodyShm, TransportMessageShm}, +}; +use zenoh_result::{bail, ZResult}; + +impl TransportUnicastShm { + #[allow(unused_mut)] // When feature "shared-memory" is not enabled + #[allow(clippy::let_and_return)] // When feature "stats" is not enabled + #[inline(always)] + pub(crate) fn internal_schedule(&self, mut msg: NetworkMessage) -> ZResult<()> { + #[cfg(feature = "shared-memory")] + { + let res = if self.config.is_shm { + crate::shm::map_zmsg_to_shminfo(&mut msg) + } else { + crate::shm::map_zmsg_to_shmbuf(&mut msg, &self.manager.shm().reader) + }; + if let Err(e) = res { + bail!("Failed SHM conversion: {}", e); + } + } + + let msg = TransportMessageShm { + body: TransportBodyShm::Network(Box::new(msg)), + }; + let res = self.send(msg); + + // #[cfg(feature = "stats")] + // if res { + // self.stats.inc_tx_z_msgs(1); + // } else { + // self.stats.inc_tx_z_dropped(1); + // } + + res + } +} diff --git a/io/zenoh-transport/src/unicast/test_helpers.rs b/io/zenoh-transport/src/unicast/test_helpers.rs new file mode 100644 index 0000000000..8e054e977a --- /dev/null +++ b/io/zenoh-transport/src/unicast/test_helpers.rs @@ -0,0 +1,50 @@ +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// + +use zenoh_core::zcondfeat; + +use crate::{TransportManager, TransportManagerBuilderUnicast}; + +pub fn make_transport_manager_builder( + #[cfg(feature = "transport_multilink")] max_links: usize, + #[cfg(feature = "shared-memory")] shm_transport: bool, +) -> TransportManagerBuilderUnicast { + let transport = make_basic_transport_manager_builder( + #[cfg(feature = "shared-memory")] + shm_transport, + ); + + zcondfeat!( + "transport_multilink", + { + println!("...with max links: {}...", max_links); + transport.max_links(max_links) + }, + transport + ) +} + +pub fn make_basic_transport_manager_builder( + #[cfg(feature = "shared-memory")] shm_transport: bool, +) -> TransportManagerBuilderUnicast { + println!("Create transport manager builder..."); + zcondfeat!( + "shared-memory", + { + println!("...with SHM..."); + TransportManager::config_unicast().shm(shm_transport) + }, + TransportManager::config_unicast() + ) +} diff --git a/io/zenoh-transport/src/unicast/transport_unicast_inner.rs b/io/zenoh-transport/src/unicast/transport_unicast_inner.rs new file mode 100644 index 0000000000..acb6503c30 --- /dev/null +++ b/io/zenoh-transport/src/unicast/transport_unicast_inner.rs @@ -0,0 +1,89 @@ +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// + +use std::{fmt::DebugStruct, sync::Arc, time::Duration}; + +use async_std::sync::MutexGuard as AsyncMutexGuard; +use async_trait::async_trait; +use zenoh_link::{LinkUnicast, LinkUnicastDirection}; +use zenoh_protocol::{ + core::{WhatAmI, ZenohId}, + network::NetworkMessage, + transport::TransportSn, +}; +use zenoh_result::ZResult; + +use crate::{TransportConfigUnicast, TransportExecutor, TransportPeerEventHandler}; + +/*************************************/ +/* UNICAST TRANSPORT TRAIT */ +/*************************************/ +#[async_trait] +pub(crate) trait TransportUnicastTrait: Send + Sync { + /*************************************/ + /* ACCESSORS */ + /*************************************/ + fn set_callback(&self, callback: Arc); + async fn get_alive(&self) -> AsyncMutexGuard<'_, bool>; + fn get_zid(&self) -> ZenohId; + fn get_whatami(&self) -> WhatAmI; + fn get_callback(&self) -> Option>; + fn get_links(&self) -> Vec; + #[cfg(feature = "shared-memory")] + fn is_shm(&self) -> bool; + fn is_qos(&self) -> bool; + fn get_config(&self) -> &TransportConfigUnicast; + #[cfg(feature = "stats")] + fn stats(&self) -> Arc; + + /*************************************/ + /* LINK */ + /*************************************/ + async fn add_link(&self, link: LinkUnicast, direction: LinkUnicastDirection) -> ZResult<()>; + + /*************************************/ + /* TX */ + /*************************************/ + fn schedule(&self, msg: NetworkMessage) -> ZResult<()>; + fn start_tx( + &self, + link: &LinkUnicast, + executor: &TransportExecutor, + keep_alive: Duration, + batch_size: u16, + ) -> ZResult<()>; + + /*************************************/ + /* RX */ + /*************************************/ + fn start_rx(&self, link: &LinkUnicast, lease: Duration, batch_size: u16) -> ZResult<()>; + + /*************************************/ + /* INITIATION */ + /*************************************/ + async fn sync(&self, _initial_sn_rx: TransportSn) -> ZResult<()>; + + /*************************************/ + /* TERMINATION */ + /*************************************/ + async fn close_link(&self, link: &LinkUnicast, reason: u8) -> ZResult<()>; + async fn close(&self, reason: u8) -> ZResult<()>; + + fn add_debug_fields<'a, 'b: 'a, 'c>( + &self, + s: &'c mut DebugStruct<'a, 'b>, + ) -> &'c mut DebugStruct<'a, 'b> { + s + } +} diff --git a/io/zenoh-transport/tests/endpoints.rs b/io/zenoh-transport/tests/endpoints.rs index 934a3b4eb0..089f8cc7f9 100644 --- a/io/zenoh-transport/tests/endpoints.rs +++ b/io/zenoh-transport/tests/endpoints.rs @@ -17,7 +17,7 @@ use zenoh_core::zasync_executor_init; use zenoh_link::{EndPoint, Link}; use zenoh_protocol::{ core::{WhatAmI, ZenohId}, - zenoh::ZenohMessage, + network::NetworkMessage, }; use zenoh_result::ZResult; use zenoh_transport::{ @@ -63,7 +63,7 @@ impl TransportEventHandler for SH { pub struct SC; impl TransportPeerEventHandler for SC { - fn handle_message(&self, _message: ZenohMessage) -> ZResult<()> { + fn handle_message(&self, _message: NetworkMessage) -> ZResult<()> { Ok(()) } fn new_link(&self, _link: Link) {} @@ -179,6 +179,24 @@ fn endpoint_ws() { task::block_on(run(&endpoints)); } +#[cfg(feature = "transport_shm")] +#[test] +fn endpoint_shm() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + // Define the locators + let endpoints: Vec = vec![ + "shm/endpoint_shm".parse().unwrap(), + "shm/endpoint_shm2".parse().unwrap(), + "shm/endpoint_shm3".parse().unwrap(), + "shm/endpoint_shm4".parse().unwrap(), + ]; + task::block_on(run(&endpoints)); +} + #[cfg(all(feature = "transport_tcp", feature = "transport_udp"))] #[test] fn endpoint_tcp_udp() { diff --git a/io/zenoh-transport/tests/multicast_transport.rs b/io/zenoh-transport/tests/multicast_transport.rs index eaf0470ef1..e9b986040f 100644 --- a/io/zenoh-transport/tests/multicast_transport.rs +++ b/io/zenoh-transport/tests/multicast_transport.rs @@ -14,21 +14,31 @@ // Restricting to macos by default because of no IPv6 support // on GitHub CI actions on Linux and Windows. -#[cfg(target_os = "macos")] +#[cfg(target_family = "unix")] mod tests { - use async_std::prelude::FutureExt; - use async_std::task; - use std::any::Any; - use std::sync::atomic::{AtomicUsize, Ordering}; - use std::sync::Arc; - use std::time::Duration; - use zenoh_buffers::ZBuf; - use zenoh_cfg_properties::config::*; + use async_std::{prelude::FutureExt, task}; + use std::{ + any::Any, + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + }, + time::Duration, + }; use zenoh_core::zasync_executor_init; use zenoh_link::Link; use zenoh_protocol::{ - core::{Channel, CongestionControl, EndPoint, Priority, Reliability, WhatAmI, ZenohId}, - zenoh::ZenohMessage, + core::{ + Channel, CongestionControl, Encoding, EndPoint, Priority, Reliability, WhatAmI, ZenohId, + }, + network::{ + push::{ + ext::{NodeIdType, QoSType}, + Push, + }, + NetworkMessage, + }, + zenoh::Put, }; use zenoh_result::ZResult; use zenoh_transport::{ @@ -64,7 +74,7 @@ mod tests { impl SHPeer { fn get_count(&self) -> usize { - self.count.load(Ordering::SeqCst) + self.count.load(Ordering::Relaxed) } } @@ -98,7 +108,8 @@ mod tests { } impl TransportMulticastEventHandler for SCPeer { - fn new_peer(&self, _peer: TransportPeer) -> ZResult> { + fn new_peer(&self, peer: TransportPeer) -> ZResult> { + println!("\tNew peer: {:?}", peer); Ok(Arc::new(SCPeer { count: self.count.clone(), })) @@ -112,8 +123,8 @@ mod tests { } impl TransportPeerEventHandler for SCPeer { - fn handle_message(&self, _msg: ZenohMessage) -> ZResult<()> { - self.count.fetch_add(1, Ordering::SeqCst); + fn handle_message(&self, _msg: NetworkMessage) -> ZResult<()> { + self.count.fetch_add(1, Ordering::Relaxed); Ok(()) } @@ -160,36 +171,50 @@ mod tests { // Open transport -> This should be accepted println!("Opening transport with {endpoint}"); let _ = ztimeout!(peer01_manager.open_transport_multicast(endpoint.clone())).unwrap(); - assert!(peer01_manager - .get_transport_multicast(&endpoint.to_locator()) - .is_some()); - println!("\t{:?}", peer01_manager.get_transports_multicast()); + assert!(!peer01_manager.get_transports_multicast().await.is_empty()); + println!("\t{:?}", peer01_manager.get_transports_multicast().await); println!("Opening transport with {endpoint}"); let _ = ztimeout!(peer02_manager.open_transport_multicast(endpoint.clone())).unwrap(); - assert!(peer02_manager - .get_transport_multicast(&endpoint.to_locator()) - .is_some()); - println!("\t{:?}", peer02_manager.get_transports_multicast()); + assert!(!peer02_manager.get_transports_multicast().await.is_empty()); + println!("\t{:?}", peer02_manager.get_transports_multicast().await); // Wait to for peer 01 and 02 to join each other - let peer01_transport = peer01_manager - .get_transport_multicast(&endpoint.to_locator()) - .unwrap(); ztimeout!(async { - while peer01_transport.get_peers().unwrap().is_empty() { + while peer01_manager + .get_transport_multicast(&peer02_id) + .await + .is_none() + { task::sleep(SLEEP_COUNT).await; } }); - - let peer02_transport = peer02_manager - .get_transport_multicast(&endpoint.to_locator()) + let peer01_transport = peer01_manager + .get_transport_multicast(&peer02_id) + .await .unwrap(); + println!( + "\tPeer01 peers: {:?}", + peer01_transport.get_peers().unwrap() + ); + ztimeout!(async { - while peer02_transport.get_peers().unwrap().is_empty() { + while peer02_manager + .get_transport_multicast(&peer01_id) + .await + .is_none() + { task::sleep(SLEEP_COUNT).await; } }); + let peer02_transport = peer02_manager + .get_transport_multicast(&peer01_id) + .await + .unwrap(); + println!( + "\tPeer02 peers: {:?}", + peer02_transport.get_peers().unwrap() + ); ( TransportMulticastPeer { @@ -213,13 +238,13 @@ mod tests { // Close the peer01 transport println!("Closing transport with {endpoint}"); ztimeout!(peer01.transport.close()).unwrap(); - assert!(peer01.manager.get_transports_multicast().is_empty()); + assert!(peer01.manager.get_transports_multicast().await.is_empty()); assert!(peer02.transport.get_peers().unwrap().is_empty()); // Close the peer02 transport println!("Closing transport with {endpoint}"); ztimeout!(peer02.transport.close()).unwrap(); - assert!(peer02.manager.get_transports_multicast().is_empty()); + assert!(peer02.manager.get_transports_multicast().await.is_empty()); // Wait a little bit task::sleep(SLEEP).await; @@ -232,22 +257,23 @@ mod tests { msg_size: usize, ) { // Create the message to send - let key = "test".into(); - let payload = ZBuf::from(vec![0_u8; msg_size]); - let data_info = None; - let routing_context = None; - let reply_context = None; - let attachment = None; - let message = ZenohMessage::make_data( - key, - payload, - channel, - CongestionControl::Block, - data_info, - routing_context, - reply_context, - attachment, - ); + let message: NetworkMessage = Push { + wire_expr: "test".into(), + ext_qos: QoSType::new(channel.priority, CongestionControl::Block, false), + ext_tstamp: None, + ext_nodeid: NodeIdType::default(), + payload: Put { + payload: vec![0u8; msg_size].into(), + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + } + .into(), + } + .into(); println!("Sending {MSG_COUNT} messages... {channel:?} {msg_size}"); for _ in 0..MSG_COUNT { @@ -281,9 +307,9 @@ mod tests { #[cfg(feature = "stats")] { - let stats = peer01.transport.get_stats().unwrap(); + let stats = peer01.transport.get_stats().unwrap().report(); println!("\tPeer 01: {:?}", stats); - let stats = peer02.transport.get_stats().unwrap(); + let stats = peer02.transport.get_stats().unwrap().report(); println!("\tPeer 02: {:?}", stats); } @@ -311,9 +337,7 @@ mod tests { // Define the locator let endpoints: Vec = vec![ - format!("udp/{ZN_MULTICAST_IPV4_ADDRESS_DEFAULT}") - .parse() - .unwrap(), + "udp/224.0.0.1:7447".parse().unwrap(), // Disabling by default because of no IPv6 support // on GitHub CI actions. // format!("udp/{}", ZN_MULTICAST_IPV6_ADDRESS_DEFAULT) diff --git a/io/zenoh-transport/tests/transport_whitelist.rs b/io/zenoh-transport/tests/transport_whitelist.rs index 6901ec2df9..338cf12429 100644 --- a/io/zenoh-transport/tests/transport_whitelist.rs +++ b/io/zenoh-transport/tests/transport_whitelist.rs @@ -17,7 +17,7 @@ use zenoh_core::zasync_executor_init; use zenoh_link::Link; use zenoh_protocol::{ core::{EndPoint, ZenohId}, - zenoh::ZenohMessage, + network::NetworkMessage, }; use zenoh_result::ZResult; use zenoh_transport::{ @@ -58,7 +58,7 @@ impl TransportEventHandler for SHRouter { pub struct SCRouter; impl TransportPeerEventHandler for SCRouter { - fn handle_message(&self, _message: ZenohMessage) -> ZResult<()> { + fn handle_message(&self, _message: NetworkMessage) -> ZResult<()> { Ok(()) } @@ -87,11 +87,11 @@ async fn run(endpoints: &[EndPoint]) { // Create the listener on the router for e in endpoints.iter() { println!("Listener endpoint: {e}"); - let res = ztimeout!(router_manager.add_listener(e.clone())); + let res = ztimeout!(router_manager.add_listener_unicast(e.clone())); assert!(res.is_err()); println!("Open endpoint: {e}"); - let res = ztimeout!(router_manager.open_transport(e.clone())); + let res = ztimeout!(router_manager.open_transport_unicast(e.clone())); assert!(res.is_err()); } @@ -110,12 +110,12 @@ async fn run(endpoints: &[EndPoint]) { // Create the listener on the router for e in endpoints.iter() { println!("Listener endpoint: {e}"); - let _ = ztimeout!(router_manager.add_listener(e.clone())).unwrap(); + let _ = ztimeout!(router_manager.add_listener_unicast(e.clone())).unwrap(); task::sleep(SLEEP).await; println!("Open endpoint: {e}"); - let _ = ztimeout!(router_manager.open_transport(e.clone())).unwrap(); + let _ = ztimeout!(router_manager.open_transport_unicast(e.clone())).unwrap(); task::sleep(SLEEP).await; } @@ -137,3 +137,21 @@ fn transport_whitelist_tcp() { // Run task::block_on(run(&endpoints)); } + +#[cfg(feature = "transport_shm")] +#[test] +#[ignore] +fn transport_whitelist_shm() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + // Define the locators + let endpoints: Vec = vec![ + "shm/transport_whitelist_shm".parse().unwrap(), + "shm/transport_whitelist_shm2".parse().unwrap(), + ]; + // Run + task::block_on(run(&endpoints)); +} diff --git a/io/zenoh-transport/tests/unicast_authenticator.rs b/io/zenoh-transport/tests/unicast_authenticator.rs index f185b513b4..33b35d52d2 100644 --- a/io/zenoh-transport/tests/unicast_authenticator.rs +++ b/io/zenoh-transport/tests/unicast_authenticator.rs @@ -12,27 +12,20 @@ // ZettaScale Zenoh Team, // use async_std::{prelude::FutureExt, task}; -#[cfg(feature = "auth_pubkey")] -use rsa::{BigUint, RsaPrivateKey, RsaPublicKey}; -#[cfg(feature = "auth_usrpwd")] -use std::collections::HashMap; -use std::{any::Any, collections::HashSet, iter::FromIterator, sync::Arc, time::Duration}; -use zenoh_core::zasync_executor_init; +use std::{any::Any, sync::Arc, time::Duration}; +use zenoh_core::{zasync_executor_init, zasyncwrite}; use zenoh_link::Link; use zenoh_protocol::{ core::{EndPoint, WhatAmI, ZenohId}, - zenoh::ZenohMessage, + network::NetworkMessage, }; use zenoh_result::ZResult; -#[cfg(feature = "auth_pubkey")] -use zenoh_transport::unicast::establishment::authenticator::PubKeyAuthenticator; -#[cfg(feature = "shared-memory")] -use zenoh_transport::unicast::establishment::authenticator::SharedMemoryAuthenticator; -#[cfg(feature = "auth_usrpwd")] -use zenoh_transport::unicast::establishment::authenticator::UserPasswordAuthenticator; use zenoh_transport::{ - DummyTransportPeerEventHandler, TransportEventHandler, TransportMulticast, - TransportMulticastEventHandler, TransportPeer, TransportPeerEventHandler, TransportUnicast, + unicast::establishment::ext::auth::Auth, TransportMulticast, TransportMulticastEventHandler, +}; +use zenoh_transport::{ + DummyTransportPeerEventHandler, TransportEventHandler, TransportPeer, + TransportPeerEventHandler, TransportUnicast, }; const TIMEOUT: Duration = Duration::from_secs(60); @@ -79,7 +72,7 @@ impl MHRouterAuthenticator { } impl TransportPeerEventHandler for MHRouterAuthenticator { - fn handle_message(&self, _msg: ZenohMessage) -> ZResult<()> { + fn handle_message(&self, _msg: NetworkMessage) -> ZResult<()> { Ok(()) } fn new_link(&self, _link: Link) {} @@ -114,61 +107,12 @@ impl TransportEventHandler for SHClientAuthenticator { } #[cfg(feature = "auth_pubkey")] -async fn authenticator_multilink(endpoint: &EndPoint) { +async fn auth_pubkey(endpoint: &EndPoint, #[cfg(feature = "shared-memory")] shm_transport: bool) { + use rsa::{BigUint, RsaPrivateKey, RsaPublicKey}; + use zenoh_transport::test_helpers::make_basic_transport_manager_builder; + use zenoh_transport::unicast::establishment::ext::auth::AuthPubKey; use zenoh_transport::TransportManager; - // Create the router transport manager - let router_id = ZenohId::try_from([1]).unwrap(); - let router_handler = Arc::new(SHRouterAuthenticator::new()); - let n = BigUint::from_bytes_le(&[ - 0x31, 0xd1, 0xfc, 0x7e, 0x70, 0x5f, 0xd7, 0xe3, 0xcc, 0xa4, 0xca, 0xcb, 0x38, 0x84, 0x2f, - 0xf5, 0x88, 0xaa, 0x4b, 0xbc, 0x2f, 0x74, 0x59, 0x49, 0xa9, 0xb9, 0x1a, 0x4d, 0x1c, 0x93, - 0xbc, 0xc7, 0x02, 0xd0, 0xe0, 0x0f, 0xa7, 0x68, 0xeb, 0xef, 0x9b, 0xf9, 0x4f, 0xdc, 0xe3, - 0x40, 0x5a, 0x3c, 0x8f, 0x20, 0xf4, 0x2c, 0x90, 0x1c, 0x70, 0x56, 0x9b, 0xae, 0x44, 0x17, - 0xca, 0x85, 0x60, 0xb5, - ]); - let e = BigUint::from_bytes_le(&[0x01, 0x00, 0x01]); - let router_pub_key = RsaPublicKey::new(n, e).unwrap(); - - let n = BigUint::from_bytes_le(&[ - 0x31, 0xd1, 0xfc, 0x7e, 0x70, 0x5f, 0xd7, 0xe3, 0xcc, 0xa4, 0xca, 0xcb, 0x38, 0x84, 0x2f, - 0xf5, 0x88, 0xaa, 0x4b, 0xbc, 0x2f, 0x74, 0x59, 0x49, 0xa9, 0xb9, 0x1a, 0x4d, 0x1c, 0x93, - 0xbc, 0xc7, 0x02, 0xd0, 0xe0, 0x0f, 0xa7, 0x68, 0xeb, 0xef, 0x9b, 0xf9, 0x4f, 0xdc, 0xe3, - 0x40, 0x5a, 0x3c, 0x8f, 0x20, 0xf4, 0x2c, 0x90, 0x1c, 0x70, 0x56, 0x9b, 0xae, 0x44, 0x17, - 0xca, 0x85, 0x60, 0xb5, - ]); - let e = BigUint::from_bytes_le(&[0x01, 0x00, 0x01]); - let d = BigUint::from_bytes_le(&[ - 0xc1, 0xd1, 0xc1, 0x0f, 0xbe, 0xa7, 0xe6, 0x18, 0x98, 0x3c, 0xf8, 0x26, 0x74, 0xc0, 0xc7, - 0xef, 0xf9, 0x38, 0x95, 0x75, 0x40, 0x45, 0xd4, 0x0d, 0x27, 0xec, 0x4c, 0xcd, 0x81, 0xf9, - 0xf4, 0x69, 0x36, 0x99, 0x95, 0x97, 0xd0, 0xc8, 0x43, 0xac, 0xbb, 0x3e, 0x8f, 0xfb, 0x97, - 0x53, 0xdb, 0x92, 0x12, 0xc5, 0xc0, 0x50, 0x83, 0xb2, 0x04, 0x25, 0x79, 0xeb, 0xa7, 0x32, - 0x84, 0xbb, 0xc6, 0x35, - ]); - let primes = vec![ - BigUint::from_bytes_le(&[ - 0xb9, 0x17, 0xd3, 0x45, 0x0a, 0x8e, 0xf7, 0x41, 0xaf, 0x75, 0xe3, 0x7f, 0xe9, 0x3c, - 0x10, 0x28, 0x24, 0x0a, 0x95, 0x32, 0xc0, 0xcb, 0x23, 0x60, 0x6e, 0x2d, 0xb8, 0x2e, - 0x96, 0x78, 0x21, 0xdf, - ]), - BigUint::from_bytes_le(&[ - 0x39, 0x51, 0xd3, 0xf3, 0xfe, 0xd1, 0x81, 0xd3, 0xc3, 0x2b, 0x49, 0x65, 0x3a, 0x44, - 0x41, 0x31, 0xa7, 0x38, 0x8b, 0xd9, 0x18, 0xc7, 0x41, 0x8c, 0x86, 0x0b, 0x65, 0x2d, - 0x18, 0x78, 0x18, 0xd0, - ]), - ]; - let router_pri_key = RsaPrivateKey::from_components(n, e, d, primes).unwrap(); - let peer_auth_router = Arc::new(PubKeyAuthenticator::new(router_pub_key, router_pri_key)); - let unicast = TransportManager::config_unicast() - .max_links(2) - .peer_authenticator(HashSet::from_iter(vec![peer_auth_router.clone().into()])); - let router_manager = TransportManager::builder() - .whatami(WhatAmI::Router) - .zid(router_id) - .unicast(unicast) - .build(router_handler.clone()) - .unwrap(); - // Create the transport transport manager for the client 01 let client01_id = ZenohId::try_from([2]).unwrap(); @@ -210,10 +154,16 @@ async fn authenticator_multilink(endpoint: &EndPoint) { ]), ]; let client01_pri_key = RsaPrivateKey::from_components(n, e, d, primes).unwrap(); - let peer_auth_client01 = PubKeyAuthenticator::new(client01_pub_key, client01_pri_key); - let unicast = TransportManager::config_unicast() - .max_links(2) - .peer_authenticator(HashSet::from_iter(vec![peer_auth_client01.into()])); + let mut auth = Auth::empty(); + auth.set_pubkey(Some(AuthPubKey::new( + client01_pub_key.clone().into(), + client01_pri_key.into(), + ))); + let unicast = make_basic_transport_manager_builder( + #[cfg(feature = "shared-memory")] + shm_transport, + ) + .authenticator(auth); let client01_manager = TransportManager::builder() .whatami(WhatAmI::Client) .zid(client01_id) @@ -262,11 +212,16 @@ async fn authenticator_multilink(endpoint: &EndPoint) { ]), ]; let client02_pri_key = RsaPrivateKey::from_components(n, e, d, primes).unwrap(); - - let peer_auth_client02 = PubKeyAuthenticator::new(client02_pub_key, client02_pri_key); - let unicast = TransportManager::config_unicast() - .max_links(2) - .peer_authenticator(HashSet::from_iter(vec![peer_auth_client02.into()])); + let mut auth = Auth::empty(); + auth.set_pubkey(Some(AuthPubKey::new( + client02_pub_key.clone().into(), + client02_pri_key.clone().into(), + ))); + let unicast = make_basic_transport_manager_builder( + #[cfg(feature = "shared-memory")] + shm_transport, + ) + .authenticator(auth); let client02_manager = TransportManager::builder() .whatami(WhatAmI::Client) .zid(client02_id) @@ -274,58 +229,83 @@ async fn authenticator_multilink(endpoint: &EndPoint) { .build(Arc::new(SHClientAuthenticator)) .unwrap(); - // Create the transport transport manager for the third client - let client01_spoof_id = client01_id; + // Create the transport transport manager for the client 03 with the same key as client 02 + let client03_id = ZenohId::try_from([4]).unwrap(); + let mut auth = Auth::empty(); + auth.set_pubkey(Some(AuthPubKey::new( + client02_pub_key.clone().into(), + client02_pri_key.clone().into(), + ))); + let unicast = make_basic_transport_manager_builder( + #[cfg(feature = "shared-memory")] + shm_transport, + ) + .authenticator(auth); + let client03_manager = TransportManager::builder() + .whatami(WhatAmI::Client) + .zid(client03_id) + .unicast(unicast) + .build(Arc::new(SHClientAuthenticator)) + .unwrap(); + // Create the router transport manager + let router_id = ZenohId::try_from([1]).unwrap(); + let router_handler = Arc::new(SHRouterAuthenticator::new()); let n = BigUint::from_bytes_le(&[ - 0x19, 0x01, 0x60, 0xf9, 0x1b, 0xd5, 0x2f, 0x1d, 0xb9, 0x16, 0xb5, 0xe4, 0xe3, 0x89, 0x7d, - 0x94, 0xa7, 0x82, 0x2f, 0xa7, 0xab, 0xec, 0xf2, 0xc2, 0x31, 0xf4, 0xf9, 0x66, 0x97, 0x6a, - 0x98, 0xf7, 0x02, 0x00, 0x0f, 0x51, 0xe4, 0xe4, 0x19, 0x29, 0xb6, 0x95, 0x1b, 0xb1, 0x2e, - 0x86, 0x2b, 0x99, 0x7f, 0x4e, 0x5a, 0x84, 0x44, 0xf3, 0xaa, 0xb3, 0xbf, 0x6c, 0xb5, 0xd4, - 0x05, 0x37, 0x33, 0xc2, + 0x31, 0xd1, 0xfc, 0x7e, 0x70, 0x5f, 0xd7, 0xe3, 0xcc, 0xa4, 0xca, 0xcb, 0x38, 0x84, 0x2f, + 0xf5, 0x88, 0xaa, 0x4b, 0xbc, 0x2f, 0x74, 0x59, 0x49, 0xa9, 0xb9, 0x1a, 0x4d, 0x1c, 0x93, + 0xbc, 0xc7, 0x02, 0xd0, 0xe0, 0x0f, 0xa7, 0x68, 0xeb, 0xef, 0x9b, 0xf9, 0x4f, 0xdc, 0xe3, + 0x40, 0x5a, 0x3c, 0x8f, 0x20, 0xf4, 0x2c, 0x90, 0x1c, 0x70, 0x56, 0x9b, 0xae, 0x44, 0x17, + 0xca, 0x85, 0x60, 0xb5, ]); let e = BigUint::from_bytes_le(&[0x01, 0x00, 0x01]); - let client01_spoof_pub_key = RsaPublicKey::new(n, e).unwrap(); + let router_pub_key = RsaPublicKey::new(n, e).unwrap(); let n = BigUint::from_bytes_le(&[ - 0x19, 0x01, 0x60, 0xf9, 0x1b, 0xd5, 0x2f, 0x1d, 0xb9, 0x16, 0xb5, 0xe4, 0xe3, 0x89, 0x7d, - 0x94, 0xa7, 0x82, 0x2f, 0xa7, 0xab, 0xec, 0xf2, 0xc2, 0x31, 0xf4, 0xf9, 0x66, 0x97, 0x6a, - 0x98, 0xf7, 0x02, 0x00, 0x0f, 0x51, 0xe4, 0xe4, 0x19, 0x29, 0xb6, 0x95, 0x1b, 0xb1, 0x2e, - 0x86, 0x2b, 0x99, 0x7f, 0x4e, 0x5a, 0x84, 0x44, 0xf3, 0xaa, 0xb3, 0xbf, 0x6c, 0xb5, 0xd4, - 0x05, 0x37, 0x33, 0xc2, + 0x31, 0xd1, 0xfc, 0x7e, 0x70, 0x5f, 0xd7, 0xe3, 0xcc, 0xa4, 0xca, 0xcb, 0x38, 0x84, 0x2f, + 0xf5, 0x88, 0xaa, 0x4b, 0xbc, 0x2f, 0x74, 0x59, 0x49, 0xa9, 0xb9, 0x1a, 0x4d, 0x1c, 0x93, + 0xbc, 0xc7, 0x02, 0xd0, 0xe0, 0x0f, 0xa7, 0x68, 0xeb, 0xef, 0x9b, 0xf9, 0x4f, 0xdc, 0xe3, + 0x40, 0x5a, 0x3c, 0x8f, 0x20, 0xf4, 0x2c, 0x90, 0x1c, 0x70, 0x56, 0x9b, 0xae, 0x44, 0x17, + 0xca, 0x85, 0x60, 0xb5, ]); let e = BigUint::from_bytes_le(&[0x01, 0x00, 0x01]); let d = BigUint::from_bytes_le(&[ - 0x01, 0xef, 0x77, 0x79, 0x99, 0xf7, 0xa7, 0x14, 0x0f, 0x61, 0xc6, 0xca, 0x3e, 0x14, 0xfa, - 0x52, 0x6a, 0xbc, 0x94, 0x2a, 0xb1, 0x7e, 0x3a, 0x57, 0xd1, 0x85, 0x62, 0xa4, 0xd0, 0xf5, - 0x40, 0x6a, 0x0f, 0xb3, 0x59, 0xec, 0x7d, 0xbd, 0xac, 0xcf, 0x28, 0x5c, 0xa1, 0x11, 0x40, - 0xfa, 0x84, 0x4a, 0x54, 0xd1, 0x7f, 0x44, 0xc8, 0xce, 0xb2, 0x21, 0x63, 0xd0, 0xc4, 0x02, - 0x55, 0x0a, 0x3a, 0x12, + 0xc1, 0xd1, 0xc1, 0x0f, 0xbe, 0xa7, 0xe6, 0x18, 0x98, 0x3c, 0xf8, 0x26, 0x74, 0xc0, 0xc7, + 0xef, 0xf9, 0x38, 0x95, 0x75, 0x40, 0x45, 0xd4, 0x0d, 0x27, 0xec, 0x4c, 0xcd, 0x81, 0xf9, + 0xf4, 0x69, 0x36, 0x99, 0x95, 0x97, 0xd0, 0xc8, 0x43, 0xac, 0xbb, 0x3e, 0x8f, 0xfb, 0x97, + 0x53, 0xdb, 0x92, 0x12, 0xc5, 0xc0, 0x50, 0x83, 0xb2, 0x04, 0x25, 0x79, 0xeb, 0xa7, 0x32, + 0x84, 0xbb, 0xc6, 0x35, ]); let primes = vec![ BigUint::from_bytes_le(&[ - 0xe1, 0x93, 0xce, 0x13, 0x6e, 0xf4, 0xb9, 0xb8, 0xea, 0xdc, 0xc9, 0x83, 0xcb, 0xe5, - 0x7d, 0x2b, 0x2f, 0x4e, 0xef, 0x75, 0x1f, 0x10, 0x4b, 0x6e, 0xbe, 0xf1, 0xc3, 0x61, - 0x33, 0x71, 0x65, 0xce, + 0xb9, 0x17, 0xd3, 0x45, 0x0a, 0x8e, 0xf7, 0x41, 0xaf, 0x75, 0xe3, 0x7f, 0xe9, 0x3c, + 0x10, 0x28, 0x24, 0x0a, 0x95, 0x32, 0xc0, 0xcb, 0x23, 0x60, 0x6e, 0x2d, 0xb8, 0x2e, + 0x96, 0x78, 0x21, 0xdf, ]), BigUint::from_bytes_le(&[ - 0x39, 0x94, 0x43, 0x4f, 0xd3, 0x74, 0x27, 0x94, 0xc7, 0x1b, 0x0f, 0xbb, 0x2b, 0xd4, - 0x9b, 0xf9, 0xe7, 0xfc, 0x47, 0x63, 0x44, 0x28, 0xa7, 0x81, 0x20, 0x91, 0x8e, 0xcf, - 0x5d, 0x66, 0xdf, 0xf0, + 0x39, 0x51, 0xd3, 0xf3, 0xfe, 0xd1, 0x81, 0xd3, 0xc3, 0x2b, 0x49, 0x65, 0x3a, 0x44, + 0x41, 0x31, 0xa7, 0x38, 0x8b, 0xd9, 0x18, 0xc7, 0x41, 0x8c, 0x86, 0x0b, 0x65, 0x2d, + 0x18, 0x78, 0x18, 0xd0, ]), ]; - let client01_spoof_pri_key = RsaPrivateKey::from_components(n, e, d, primes).unwrap(); - - let peer_auth_client01_spoof = - PubKeyAuthenticator::new(client01_spoof_pub_key, client01_spoof_pri_key); - let unicast = TransportManager::config_unicast() - .max_links(2) - .peer_authenticator(HashSet::from_iter(vec![peer_auth_client01_spoof.into()])); - let client01_spoof_manager = TransportManager::builder() - .whatami(WhatAmI::Client) - .zid(client01_spoof_id) + let router_pri_key = RsaPrivateKey::from_components(n, e, d, primes).unwrap(); + let mut auth_pubkey = AuthPubKey::new(router_pub_key.into(), router_pri_key.into()); + auth_pubkey + .add_pubkey(client01_pub_key.into()) + .await + .unwrap(); + let mut auth = Auth::empty(); + auth.set_pubkey(Some(auth_pubkey)); + let unicast = make_basic_transport_manager_builder( + #[cfg(feature = "shared-memory")] + shm_transport, + ) + .authenticator(auth); + let router_manager = TransportManager::builder() + .whatami(WhatAmI::Router) + .zid(router_id) .unicast(unicast) - .build(Arc::new(SHClientAuthenticator)) + .build(router_handler.clone()) .unwrap(); /* [1] */ @@ -341,161 +321,73 @@ async fn authenticator_multilink(endpoint: &EndPoint) { // Open a first transport from client01 to the router // -> This should be accepted println!("Transport Authenticator PubKey [2a1]"); - let c_ses1 = ztimeout!(client01_manager.open_transport(endpoint.clone())).unwrap(); + let c_ses1 = ztimeout!(client01_manager.open_transport_unicast(endpoint.clone())).unwrap(); assert_eq!(c_ses1.get_links().unwrap().len(), 1); /* [2b] */ - // Open a second transport from client01 to the router - // -> This should be accepted + // Open a first transport from client02 to the router + // -> This should be rejected println!("Transport Authenticator PubKey [2b1]"); - let c_ses1_tmp = ztimeout!(client01_manager.open_transport(endpoint.clone())).unwrap(); - assert_eq!(c_ses1, c_ses1_tmp); - assert_eq!(c_ses1.get_links().unwrap().len(), 2); + let res = ztimeout!(client02_manager.open_transport_unicast(endpoint.clone())); + println!("Transport Authenticator PubKey [2b2]: {res:?}"); + assert!(res.is_err()); /* [2c] */ - // Open a third transport from client01 to the router + // Open a first transport from client03 to the router // -> This should be rejected println!("Transport Authenticator PubKey [2c1]"); - let res = ztimeout!(client01_manager.open_transport(endpoint.clone())); + let res = ztimeout!(client03_manager.open_transport_unicast(endpoint.clone())); println!("Transport Authenticator PubKey [2c2]: {res:?}"); assert!(res.is_err()); - assert_eq!(c_ses1.get_links().unwrap().len(), 2); - /* [2d] */ - // Close the session - println!("Transport Authenticator PubKey [2d1]"); - ztimeout!(c_ses1.close()).unwrap(); - - ztimeout!(async { - while !router_manager.get_transports().is_empty() { - task::sleep(SLEEP).await; - } - }); + // Add client02 pubkey to the router + let router_auth_handle = router_manager.get_auth_handle_unicast(); + zasyncwrite!(router_auth_handle.get_pubkey().unwrap()) + .add_pubkey(client02_pub_key.into()) + .await + .unwrap(); - /* [3a] */ + /* [3b] */ // Open a first transport from client02 to the router // -> This should be accepted println!("Transport Authenticator PubKey [3a1]"); - let c_ses2 = ztimeout!(client02_manager.open_transport(endpoint.clone())).unwrap(); + let c_ses2 = ztimeout!(client02_manager.open_transport_unicast(endpoint.clone())).unwrap(); assert_eq!(c_ses2.get_links().unwrap().len(), 1); - /* [3b] */ - // Open a second transport from client02 to the router + // Open a first transport from client03 to the router // -> This should be accepted println!("Transport Authenticator PubKey [3b1]"); - let c_ses2_tmp = ztimeout!(client02_manager.open_transport(endpoint.clone())).unwrap(); - assert_eq!(c_ses2, c_ses2_tmp); - assert_eq!(c_ses2.get_links().unwrap().len(), 2); - - /* [3c] */ - // Open a third transport from client02 to the router - // -> This should be rejected - println!("Transport Authenticator PubKey [3c1]"); - let res = ztimeout!(client02_manager.open_transport(endpoint.clone())); - println!("Transport Authenticator PubKey [3c2]: {res:?}"); - assert!(res.is_err()); - assert_eq!(c_ses2.get_links().unwrap().len(), 2); - - /* [3d] */ - // Close the session - println!("Transport Authenticator PubKey [3d1]"); - let res = ztimeout!(c_ses2.close()); - println!("Transport Authenticator PubKey [3d2]: {res:?}"); - assert!(res.is_ok()); - - ztimeout!(async { - while !router_manager.get_transports().is_empty() { - task::sleep(SLEEP).await; - } - }); + let c_ses3 = ztimeout!(client03_manager.open_transport_unicast(endpoint.clone())).unwrap(); + assert_eq!(c_ses3.get_links().unwrap().len(), 1); - /* [4a] */ - // Open a first transport from client01_spoof to the router - // -> This should be accepted + // /* [4a] */ + // Close the sessions println!("Transport Authenticator PubKey [4a1]"); - let res = ztimeout!(client01_spoof_manager.open_transport(endpoint.clone())); + let res = ztimeout!(c_ses1.close()); println!("Transport Authenticator PubKey [4a2]: {res:?}"); assert!(res.is_ok()); - let c_ses1_spoof = res.unwrap(); - assert_eq!(c_ses1_spoof.get_links().unwrap().len(), 1); - /* [4b] */ - // Open a second transport from client01_spoof to the router - // -> This should be accepted println!("Transport Authenticator PubKey [4b1]"); - let res = ztimeout!(client01_spoof_manager.open_transport(endpoint.clone())); + let res = ztimeout!(c_ses2.close()); println!("Transport Authenticator PubKey [4b2]: {res:?}"); assert!(res.is_ok()); - assert_eq!(c_ses1_spoof.get_links().unwrap().len(), 2); - /* [4c] */ - // Open a third transport from client02 to the router - // -> This should be rejected - println!("Transport Authenticator PubKey [41]"); - let res = ztimeout!(client01_spoof_manager.open_transport(endpoint.clone())); + println!("Transport Authenticator PubKey [4c1]"); + let res = ztimeout!(c_ses3.close()); println!("Transport Authenticator PubKey [4c2]: {res:?}"); - assert!(res.is_err()); - assert_eq!(c_ses1_spoof.get_links().unwrap().len(), 2); - - /* [4d] */ - // Close the session - println!("Transport Authenticator PubKey [4d1]"); - let res = ztimeout!(c_ses1_spoof.close()); - println!("Transport Authenticator PubKey [4d2]: {res:?}"); - assert!(res.is_ok()); - - ztimeout!(async { - while !router_manager.get_transports().is_empty() { - task::sleep(SLEEP).await; - } - }); - - /* [5a] */ - // Open a first transport from client01 to the router - // -> This should be accepted - println!("Transport Authenticator PubKey [5a1]"); - let res = ztimeout!(client01_manager.open_transport(endpoint.clone())); - println!("Transport Authenticator PubKey [5a2]: {res:?}"); - assert!(res.is_ok()); - let c_ses1 = res.unwrap(); - assert_eq!(c_ses1.get_links().unwrap().len(), 1); - - /* [5b] */ - // Open a spoof transport from client01_spoof to the router - // -> This should be rejected. Spoofing detected. - println!("Transport Authenticator PubKey [5b1]"); - let res = ztimeout!(client01_spoof_manager.open_transport(endpoint.clone())); - println!("Transport Authenticator PubKey [5b2]: {res:?}"); - assert!(res.is_err()); - - /* [5c] */ - // Open a second transport from client01 to the router - // -> This should be accepted - println!("Transport Authenticator PubKey [5a1]"); - let res = ztimeout!(client01_manager.open_transport(endpoint.clone())); - println!("Transport Authenticator PubKey [5a2]: {res:?}"); - assert!(res.is_ok()); - let c_ses1 = res.unwrap(); - assert_eq!(c_ses1.get_links().unwrap().len(), 2); - - /* [5d] */ - // Close the session - println!("Transport Authenticator PubKey [5d1]"); - let res = ztimeout!(c_ses1.close()); - println!("Transport Authenticator PubKey [5d2]: {res:?}"); assert!(res.is_ok()); ztimeout!(async { - while !router_manager.get_transports().is_empty() { + while !router_manager.get_transports_unicast().await.is_empty() { task::sleep(SLEEP).await; } }); - /* [6] */ + /* [5] */ // Perform clean up of the open locators - println!("Transport Authenticator UserPassword [6a1]"); + println!("Transport Authenticator PubKey [5a1]"); let res = ztimeout!(router_manager.del_listener(endpoint)); - println!("Transport Authenticator UserPassword [6a2]: {res:?}"); + println!("Transport Authenticator PubKey [5a2]: {res:?}"); assert!(res.is_ok()); ztimeout!(async { @@ -504,16 +396,19 @@ async fn authenticator_multilink(endpoint: &EndPoint) { } }); - ztimeout!(router_manager.close()); ztimeout!(client01_manager.close()); - ztimeout!(client01_spoof_manager.close()); + ztimeout!(client02_manager.close()); + ztimeout!(client03_manager.close()); + ztimeout!(router_manager.close()); // Wait a little bit task::sleep(SLEEP).await; } #[cfg(feature = "auth_usrpwd")] -async fn authenticator_user_password(endpoint: &EndPoint) { +async fn auth_usrpwd(endpoint: &EndPoint, #[cfg(feature = "shared-memory")] shm_transport: bool) { + use zenoh_transport::test_helpers::make_basic_transport_manager_builder; + use zenoh_transport::unicast::establishment::ext::auth::AuthUsrPwd; use zenoh_transport::TransportManager; /* [CLIENT] */ @@ -533,13 +428,23 @@ async fn authenticator_user_password(endpoint: &EndPoint) { let router_id = ZenohId::try_from([1]).unwrap(); let router_handler = Arc::new(SHRouterAuthenticator::new()); // Create the router transport manager - let mut lookup: HashMap, Vec> = HashMap::new(); - lookup.insert(user01.clone().into(), password01.clone().into()); - lookup.insert(user03.clone().into(), password03.clone().into()); - - let peer_auth_router = Arc::new(UserPasswordAuthenticator::new(lookup, None)); - let unicast = TransportManager::config_unicast() - .peer_authenticator(HashSet::from_iter(vec![peer_auth_router.clone().into()])); + let mut auth_usrpwd_router = AuthUsrPwd::new(None); + auth_usrpwd_router + .add_user(user01.clone().into(), password01.clone().into()) + .await + .unwrap(); + auth_usrpwd_router + .add_user(user03.clone().into(), password03.clone().into()) + .await + .unwrap(); + let mut auth_router = Auth::empty(); + auth_router.set_usrpwd(Some(auth_usrpwd_router)); + + let unicast = make_basic_transport_manager_builder( + #[cfg(feature = "shared-memory")] + shm_transport, + ) + .authenticator(auth_router); let router_manager = TransportManager::builder() .whatami(WhatAmI::Router) .zid(router_id) @@ -548,13 +453,15 @@ async fn authenticator_user_password(endpoint: &EndPoint) { .unwrap(); // Create the transport transport manager for the first client - let lookup: HashMap, Vec> = HashMap::new(); - let peer_auth_client01 = UserPasswordAuthenticator::new( - lookup, - Some((user01.clone().into(), password01.clone().into())), - ); - let unicast = TransportManager::config_unicast() - .peer_authenticator(HashSet::from_iter(vec![peer_auth_client01.into()])); + let auth_usrpwdr_client01 = + AuthUsrPwd::new(Some((user01.clone().into(), password01.clone().into()))); + let mut auth_client01 = Auth::empty(); + auth_client01.set_usrpwd(Some(auth_usrpwdr_client01)); + let unicast = make_basic_transport_manager_builder( + #[cfg(feature = "shared-memory")] + shm_transport, + ) + .authenticator(auth_client01); let client01_manager = TransportManager::builder() .whatami(WhatAmI::Client) .zid(client01_id) @@ -563,13 +470,15 @@ async fn authenticator_user_password(endpoint: &EndPoint) { .unwrap(); // Create the transport transport manager for the second client - let lookup: HashMap, Vec> = HashMap::new(); - let peer_auth_client02 = UserPasswordAuthenticator::new( - lookup, - Some((user02.clone().into(), password02.clone().into())), - ); - let unicast = TransportManager::config_unicast() - .peer_authenticator(HashSet::from_iter(vec![peer_auth_client02.into()])); + let auth_usrpwdr_client02 = + AuthUsrPwd::new(Some((user02.clone().into(), password02.clone().into()))); + let mut auth_client02 = Auth::empty(); + auth_client02.set_usrpwd(Some(auth_usrpwdr_client02)); + let unicast = make_basic_transport_manager_builder( + #[cfg(feature = "shared-memory")] + shm_transport, + ) + .authenticator(auth_client02); let client02_manager = TransportManager::builder() .whatami(WhatAmI::Client) .zid(client02_id) @@ -578,13 +487,15 @@ async fn authenticator_user_password(endpoint: &EndPoint) { .unwrap(); // Create the transport transport manager for the third client - let lookup: HashMap, Vec> = HashMap::new(); - let peer_auth_client03 = UserPasswordAuthenticator::new( - lookup, - Some((user03.clone().into(), password03.clone().into())), - ); - let unicast = TransportManager::config_unicast() - .peer_authenticator(HashSet::from_iter(vec![peer_auth_client03.into()])); + let auth_usrpwdr_client03 = + AuthUsrPwd::new(Some((user03.clone().into(), password03.clone().into()))); + let mut auth_client03 = Auth::empty(); + auth_client03.set_usrpwd(Some(auth_usrpwdr_client03)); + let unicast = make_basic_transport_manager_builder( + #[cfg(feature = "shared-memory")] + shm_transport, + ) + .authenticator(auth_client03); let client03_manager = TransportManager::builder() .whatami(WhatAmI::Client) .zid(client03_id) @@ -607,7 +518,7 @@ async fn authenticator_user_password(endpoint: &EndPoint) { // Open a first transport from the client to the router // -> This should be accepted println!("Transport Authenticator UserPassword [2a1]"); - let res = ztimeout!(client01_manager.open_transport(endpoint.clone())); + let res = ztimeout!(client01_manager.open_transport_unicast(endpoint.clone())); println!("Transport Authenticator UserPassword [2a1]: {res:?}"); assert!(res.is_ok()); let c_ses1 = res.unwrap(); @@ -619,7 +530,7 @@ async fn authenticator_user_password(endpoint: &EndPoint) { assert!(res.is_ok()); ztimeout!(async { - while !router_manager.get_transports().is_empty() { + while !router_manager.get_transports_unicast().await.is_empty() { task::sleep(SLEEP).await; } }); @@ -628,7 +539,7 @@ async fn authenticator_user_password(endpoint: &EndPoint) { // Open a second transport from the client to the router // -> This should be rejected println!("Transport Authenticator UserPassword [4a1]"); - let res = ztimeout!(client02_manager.open_transport(endpoint.clone())); + let res = ztimeout!(client02_manager.open_transport_unicast(endpoint.clone())); println!("Transport Authenticator UserPassword [4a1]: {res:?}"); assert!(res.is_err()); @@ -636,19 +547,23 @@ async fn authenticator_user_password(endpoint: &EndPoint) { // Open a third transport from the client to the router // -> This should be accepted println!("Transport Authenticator UserPassword [5a1]"); - let res = ztimeout!(client01_manager.open_transport(endpoint.clone())); + let res = ztimeout!(client01_manager.open_transport_unicast(endpoint.clone())); println!("Transport Authenticator UserPassword [5a1]: {res:?}"); assert!(res.is_ok()); let c_ses1 = res.unwrap(); /* [6] */ // Add client02 credentials on the router - let res = ztimeout!(peer_auth_router.add_user(user02.into(), password02.into())); - assert!(res.is_ok()); + let auth_router = router_manager.get_auth_handle_unicast(); + ztimeout!( + zasyncwrite!(auth_router.get_usrpwd().unwrap()).add_user(user02.into(), password02.into()) + ) + .unwrap(); + // Open a fourth transport from the client to the router // -> This should be accepted println!("Transport Authenticator UserPassword [6a1]"); - let res = ztimeout!(client02_manager.open_transport(endpoint.clone())); + let res = ztimeout!(client02_manager.open_transport_unicast(endpoint.clone())); println!("Transport Authenticator UserPassword [6a1]: {res:?}"); assert!(res.is_ok()); let c_ses2 = res.unwrap(); @@ -657,7 +572,7 @@ async fn authenticator_user_password(endpoint: &EndPoint) { // Open a fourth transport from the client to the router // -> This should be rejected println!("Transport Authenticator UserPassword [7a1]"); - let res = ztimeout!(client03_manager.open_transport(endpoint.clone())); + let res = ztimeout!(client03_manager.open_transport_unicast(endpoint.clone())); println!("Transport Authenticator UserPassword [7a1]: {res:?}"); assert!(res.is_err()); @@ -672,7 +587,7 @@ async fn authenticator_user_password(endpoint: &EndPoint) { assert!(res.is_ok()); ztimeout!(async { - while !router_manager.get_transports().is_empty() { + while !router_manager.get_transports_unicast().await.is_empty() { task::sleep(SLEEP).await; } }); @@ -694,122 +609,114 @@ async fn authenticator_user_password(endpoint: &EndPoint) { task::sleep(SLEEP).await; } -#[cfg(feature = "shared-memory")] -async fn authenticator_shared_memory(endpoint: &EndPoint) { - use zenoh_transport::TransportManager; - - /* [CLIENT] */ - let client_id = ZenohId::try_from([2]).unwrap(); - - /* [ROUTER] */ - let router_id = ZenohId::try_from([1]).unwrap(); - let router_handler = Arc::new(SHRouterAuthenticator::new()); - // Create the router transport manager - let peer_auth_router = SharedMemoryAuthenticator::make().unwrap(); - let unicast = TransportManager::config_unicast() - .peer_authenticator(HashSet::from_iter(vec![peer_auth_router.into()])); - let router_manager = TransportManager::builder() - .whatami(WhatAmI::Router) - .zid(router_id) - .unicast(unicast) - .build(router_handler.clone()) - .unwrap(); +async fn run(endpoint: &EndPoint, #[cfg(feature = "shared-memory")] shm_transport: bool) { + #[cfg(feature = "auth_pubkey")] + auth_pubkey( + endpoint, + #[cfg(feature = "shared-memory")] + shm_transport, + ) + .await; + #[cfg(feature = "auth_usrpwd")] + auth_usrpwd( + endpoint, + #[cfg(feature = "shared-memory")] + shm_transport, + ) + .await; +} - // Create the transport transport manager for the first client - let peer_auth_client = SharedMemoryAuthenticator::make().unwrap(); - let unicast = TransportManager::config_unicast() - .peer_authenticator(HashSet::from_iter(vec![peer_auth_client.into()])); - let client_manager = TransportManager::builder() - .whatami(WhatAmI::Router) - .zid(client_id) - .unicast(unicast) - .build(Arc::new(SHClientAuthenticator)) - .unwrap(); +async fn run_with_net(endpoint: &EndPoint) { + run( + endpoint, + #[cfg(feature = "shared-memory")] + false, + ) + .await +} - /* [1] */ - println!("\nTransport Authenticator SharedMemory [1a1]"); - // Add the locator on the router - let res = ztimeout!(router_manager.add_listener(endpoint.clone())); - println!("Transport Authenticator SharedMemory [1a1]: {res:?}"); - assert!(res.is_ok()); - println!("Transport Authenticator SharedMemory [1a2]"); - let locators = router_manager.get_listeners(); - println!("Transport Authenticator SharedMemory 1a2]: {locators:?}"); - assert_eq!(locators.len(), 1); +#[cfg(feature = "shared-memory")] +async fn run_with_shm(endpoint: &EndPoint) { + run(endpoint, true).await +} - /* [2] */ - // Open a transport from the client to the router - // -> This should be accepted - println!("Transport Authenticator SharedMemory [2a1]"); - let res = ztimeout!(client_manager.open_transport(endpoint.clone())); - println!("Transport Authenticator SharedMemory [2a1]: {res:?}"); - assert!(res.is_ok()); - let c_ses1 = res.unwrap(); - assert!(c_ses1.is_shm().unwrap()); +#[cfg(feature = "transport_tcp")] +#[test] +fn authenticator_tcp() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); - /* [3] */ - println!("Transport Authenticator SharedMemory [3a1]"); - let res = ztimeout!(c_ses1.close()); - println!("Transport Authenticator SharedMemory [3a1]: {res:?}"); - assert!(res.is_ok()); + let endpoint: EndPoint = format!("tcp/127.0.0.1:{}", 8000).parse().unwrap(); + task::block_on(run_with_net(&endpoint)); +} - ztimeout!(async { - while !router_manager.get_transports().is_empty() { - task::sleep(SLEEP).await; - } +#[cfg(all(feature = "transport_tcp", feature = "shared-memory"))] +#[test] +fn authenticator_tcp_with_shm_transport() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); }); - /* [4] */ - // Perform clean up of the open locators - println!("Transport Authenticator SharedMemory [4a1]"); - let res = ztimeout!(router_manager.del_listener(endpoint)); - println!("Transport Authenticator SharedMemory [4a2]: {res:?}"); - assert!(res.is_ok()); + let endpoint: EndPoint = format!("tcp/127.0.0.1:{}", 8100).parse().unwrap(); + task::block_on(run_with_shm(&endpoint)); +} - ztimeout!(async { - while !router_manager.get_listeners().is_empty() { - task::sleep(SLEEP).await; - } +#[cfg(feature = "transport_udp")] +#[test] +fn authenticator_udp() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); }); - task::sleep(SLEEP).await; + let endpoint: EndPoint = format!("udp/127.0.0.1:{}", 8010).parse().unwrap(); + task::block_on(run_with_net(&endpoint)); } -async fn run(endpoint: &EndPoint) { - #[cfg(feature = "auth_pubkey")] - authenticator_multilink(endpoint).await; - #[cfg(feature = "auth_usrpwd")] - authenticator_user_password(endpoint).await; - #[cfg(feature = "shared-memory")] - authenticator_shared_memory(endpoint).await; +#[cfg(all(feature = "transport_udp", feature = "shared-memory"))] +#[test] +fn authenticator_udp_with_shm_transport() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + let endpoint: EndPoint = format!("udp/127.0.0.1:{}", 8110).parse().unwrap(); + task::block_on(run_with_shm(&endpoint)); } -#[cfg(feature = "transport_tcp")] +#[cfg(feature = "transport_shm")] #[test] -fn authenticator_tcp() { +#[ignore] +fn authenticator_shm() { let _ = env_logger::try_init(); task::block_on(async { zasync_executor_init!(); }); - let endpoint: EndPoint = format!("tcp/127.0.0.1:{}", 8000).parse().unwrap(); - task::block_on(run(&endpoint)); + let endpoint: EndPoint = "shm/authenticator_shm_test".parse().unwrap(); + task::block_on(run_with_net(&endpoint)); } -#[cfg(feature = "transport_udp")] +#[cfg(all(feature = "transport_shm", feature = "shared-memory"))] #[test] -fn authenticator_udp() { +#[ignore] +fn authenticator_shm_with_shm_transport() { let _ = env_logger::try_init(); task::block_on(async { zasync_executor_init!(); }); - let endpoint: EndPoint = format!("udp/127.0.0.1:{}", 8010).parse().unwrap(); - task::block_on(run(&endpoint)); + let endpoint: EndPoint = "shm/authenticator_shm_with_shm_transport".parse().unwrap(); + task::block_on(run_with_shm(&endpoint)); } #[cfg(feature = "transport_ws")] #[test] +#[ignore] fn authenticator_ws() { let _ = env_logger::try_init(); task::block_on(async { @@ -817,7 +724,20 @@ fn authenticator_ws() { }); let endpoint: EndPoint = format!("ws/127.0.0.1:{}", 8020).parse().unwrap(); - task::block_on(run(&endpoint)); + task::block_on(run_with_net(&endpoint)); +} + +#[cfg(all(feature = "transport_ws", feature = "shared-memory"))] +#[test] +#[ignore] +fn authenticator_ws_with_shm_transport() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + let endpoint: EndPoint = format!("ws/127.0.0.1:{}", 8120).parse().unwrap(); + task::block_on(run_with_shm(&endpoint)); } #[cfg(all(feature = "transport_unixsock-stream", target_family = "unix"))] @@ -831,7 +751,7 @@ fn authenticator_unix() { let f1 = "zenoh-test-unix-socket-10.sock"; let _ = std::fs::remove_file(f1); let endpoint: EndPoint = format!("unixsock-stream/{f1}").parse().unwrap(); - task::block_on(run(&endpoint)); + task::block_on(run_with_net(&endpoint)); let _ = std::fs::remove_file(f1); let _ = std::fs::remove_file(format!("{f1}.lock")); } @@ -936,7 +856,7 @@ R+IdLiXcyIkg0m9N8I17p0ljCSkbrgGMD3bbePRTfg== ) .unwrap(); - task::block_on(run(&endpoint)); + task::block_on(run_with_net(&endpoint)); } #[cfg(feature = "transport_quic")] @@ -1039,5 +959,5 @@ R+IdLiXcyIkg0m9N8I17p0ljCSkbrgGMD3bbePRTfg== ) .unwrap(); - task::block_on(run(&endpoint)); + task::block_on(run_with_net(&endpoint)); } diff --git a/io/zenoh-transport/tests/unicast_concurrent.rs b/io/zenoh-transport/tests/unicast_concurrent.rs index 244cd8b074..77cc7eadf2 100644 --- a/io/zenoh-transport/tests/unicast_concurrent.rs +++ b/io/zenoh-transport/tests/unicast_concurrent.rs @@ -1,4 +1,3 @@ -// // Copyright (c) 2023 ZettaScale Technology // // This program and the accompanying materials are made available under the @@ -19,12 +18,18 @@ use std::convert::TryFrom; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::time::Duration; -use zenoh_buffers::ZBuf; use zenoh_core::zasync_executor_init; use zenoh_link::Link; use zenoh_protocol::{ - core::{Channel, CongestionControl, EndPoint, Priority, Reliability, WhatAmI, ZenohId}, - zenoh::ZenohMessage, + core::{CongestionControl, Encoding, EndPoint, Priority, WhatAmI, ZenohId}, + network::{ + push::{ + ext::{NodeIdType, QoSType}, + Push, + }, + NetworkMessage, + }, + zenoh::Put, }; use zenoh_result::ZResult; use zenoh_transport::{ @@ -89,7 +94,7 @@ impl MHPeer { } impl TransportPeerEventHandler for MHPeer { - fn handle_message(&self, _msg: ZenohMessage) -> ZResult<()> { + fn handle_message(&self, _msg: NetworkMessage) -> ZResult<()> { self.count.fetch_add(1, Ordering::AcqRel); Ok(()) } @@ -162,7 +167,7 @@ async fn transport_concurrent(endpoint01: Vec, endpoint02: Vec Waiting for opening transport"); // Syncrhonize before opening the transports ztimeout!(cc_barow.wait()); - let res = ztimeout!(c_p01m.open_transport(c_end.clone())); + let res = ztimeout!(c_p01m.open_transport_unicast(c_end.clone())); println!("[Transport Peer 01d] => Opening transport with {c_end:?}: {res:?}"); assert!(res.is_ok()); @@ -176,43 +181,41 @@ async fn transport_concurrent(endpoint01: Vec, endpoint02: Vec Waiting... OK"); // Verify that the transport has been correctly open - assert_eq!(peer01_manager.get_transports().len(), 1); - let s02 = peer01_manager.get_transport(&c_zid02).unwrap(); + assert_eq!(peer01_manager.get_transports_unicast().await.len(), 1); + let s02 = peer01_manager + .get_transport_unicast(&c_zid02) + .await + .unwrap(); assert_eq!( s02.get_links().unwrap().len(), c_end01.len() + c_end02.len() ); // Create the message to send - let key = "test02".into(); - let payload = ZBuf::from(vec![0_u8; MSG_SIZE]); - let channel = Channel { - priority: Priority::default(), - reliability: Reliability::Reliable, - }; - let congestion_control = CongestionControl::Block; - let data_info = None; - let routing_context = None; - let reply_context = None; - let attachment = None; - - let message = ZenohMessage::make_data( - key, - payload, - channel, - congestion_control, - data_info, - routing_context, - reply_context, - attachment, - ); + let message: NetworkMessage = Push { + wire_expr: "test".into(), + ext_qos: QoSType::new(Priority::default(), CongestionControl::Block, false), + ext_tstamp: None, + ext_nodeid: NodeIdType::default(), + payload: Put { + payload: vec![0u8; MSG_SIZE].into(), + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + } + .into(), + } + .into(); // Synchronize wit the peer ztimeout!(c_barp.wait()); println!("[Transport Peer 01f] => Waiting... OK"); for i in 0..MSG_COUNT { - println!("[Transport Peer 01g] Scheduling message {i}"); + println!("[Transport Peer 01g] Scheduling message {}", i); s02.schedule(message.clone()).unwrap(); } println!("[Transport Peer 01g] => Scheduling OK"); @@ -265,7 +268,7 @@ async fn transport_concurrent(endpoint01: Vec, endpoint02: Vec Opening transport with {c_end:?}: {res:?}"); assert!(res.is_ok()); @@ -280,45 +283,43 @@ async fn transport_concurrent(endpoint01: Vec, endpoint02: Vec Transports: {:?}", - peer02_manager.get_transports() + peer02_manager.get_transports_unicast().await ); - assert_eq!(peer02_manager.get_transports().len(), 1); - let s01 = peer02_manager.get_transport(&c_zid01).unwrap(); + assert_eq!(peer02_manager.get_transports_unicast().await.len(), 1); + let s01 = peer02_manager + .get_transport_unicast(&c_zid01) + .await + .unwrap(); assert_eq!( s01.get_links().unwrap().len(), c_end01.len() + c_end02.len() ); // Create the message to send - let key = "test02".into(); - let payload = ZBuf::from(vec![0_u8; MSG_SIZE]); - let channel = Channel { - priority: Priority::default(), - reliability: Reliability::Reliable, - }; - let congestion_control = CongestionControl::Block; - let data_info = None; - let routing_context = None; - let reply_context = None; - let attachment = None; - - let message = ZenohMessage::make_data( - key, - payload, - channel, - congestion_control, - data_info, - routing_context, - reply_context, - attachment, - ); + let message: NetworkMessage = Push { + wire_expr: "test".into(), + ext_qos: QoSType::new(Priority::default(), CongestionControl::Block, false), + ext_tstamp: None, + ext_nodeid: NodeIdType::default(), + payload: Put { + payload: vec![0u8; MSG_SIZE].into(), + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + } + .into(), + } + .into(); // Synchronize wit the peer ztimeout!(c_barp.wait()); println!("[Transport Peer 02f] => Waiting... OK"); for i in 0..MSG_COUNT { - println!("[Transport Peer 02g] Scheduling message {i}"); + println!("[Transport Peer 02g] Scheduling message {}", i); s01.schedule(message.clone()).unwrap(); } println!("[Transport Peer 02g] => Scheduling OK"); @@ -386,6 +387,7 @@ fn transport_tcp_concurrent() { #[cfg(feature = "transport_ws")] #[test] +#[ignore] fn transport_ws_concurrent() { let _ = env_logger::try_init(); task::block_on(async { @@ -417,3 +419,38 @@ fn transport_ws_concurrent() { transport_concurrent(endpoint01, endpoint02).await; }); } + +#[cfg(feature = "transport_shm")] +#[test] +#[ignore] +fn transport_shm_concurrent() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + let endpoint01: Vec = vec![ + "shm/transport_shm_concurrent".parse().unwrap(), + "shm/transport_shm_concurrent2".parse().unwrap(), + "shm/transport_shm_concurrent3".parse().unwrap(), + "shm/transport_shm_concurrent4".parse().unwrap(), + "shm/transport_shm_concurrent5".parse().unwrap(), + "shm/transport_shm_concurrent6".parse().unwrap(), + "shm/transport_shm_concurrent7".parse().unwrap(), + "shm/transport_shm_concurrent8".parse().unwrap(), + ]; + let endpoint02: Vec = vec![ + "shm/transport_shm_concurrent9".parse().unwrap(), + "shm/transport_shm_concurrent10".parse().unwrap(), + "shm/transport_shm_concurrent11".parse().unwrap(), + "shm/transport_shm_concurrent12".parse().unwrap(), + "shm/transport_shm_concurrent13".parse().unwrap(), + "shm/transport_shm_concurrent14".parse().unwrap(), + "shm/transport_shm_concurrent15".parse().unwrap(), + "shm/transport_shm_concurrent16".parse().unwrap(), + ]; + + task::block_on(async { + transport_concurrent(endpoint01, endpoint02).await; + }); +} diff --git a/io/zenoh-transport/tests/unicast_defragmentation.rs b/io/zenoh-transport/tests/unicast_defragmentation.rs index 1a58bb762b..d85a0c021e 100644 --- a/io/zenoh-transport/tests/unicast_defragmentation.rs +++ b/io/zenoh-transport/tests/unicast_defragmentation.rs @@ -13,11 +13,19 @@ // use async_std::{prelude::FutureExt, task}; use std::{convert::TryFrom, sync::Arc, time::Duration}; -use zenoh_buffers::ZBuf; use zenoh_core::zasync_executor_init; use zenoh_protocol::{ - core::{Channel, CongestionControl, EndPoint, Priority, Reliability, WhatAmI, ZenohId}, - zenoh::ZenohMessage, + core::{ + Channel, CongestionControl, Encoding, EndPoint, Priority, Reliability, WhatAmI, ZenohId, + }, + network::{ + push::{ + ext::{NodeIdType, QoSType}, + Push, + }, + NetworkMessage, + }, + zenoh::Put, }; use zenoh_transport::{DummyTransportEventHandler, TransportManager}; @@ -61,27 +69,31 @@ async fn run(endpoint: &EndPoint, channel: Channel, msg_size: usize) { // Create an empty transport with the client // Open transport -> This should be accepted println!("Opening transport with {endpoint}"); - let _ = ztimeout!(client_manager.open_transport(endpoint.clone())).unwrap(); - - let client_transport = client_manager.get_transport(&router_id).unwrap(); - - // Create the message to send, this would trigger the transport closure - let key = "test".into(); - let payload = ZBuf::from(vec![0_u8; msg_size]); - let data_info = None; - let routing_context = None; - let reply_context = None; - let attachment = None; - let message = ZenohMessage::make_data( - key, - payload, - channel, - CongestionControl::Block, - data_info, - routing_context, - reply_context, - attachment, - ); + let _ = ztimeout!(client_manager.open_transport_unicast(endpoint.clone())).unwrap(); + + let client_transport = client_manager + .get_transport_unicast(&router_id) + .await + .unwrap(); + + // Create the message to send + let message: NetworkMessage = Push { + wire_expr: "test".into(), + ext_qos: QoSType::new(channel.priority, CongestionControl::Block, false), + ext_tstamp: None, + ext_nodeid: NodeIdType::default(), + payload: Put { + payload: vec![0u8; msg_size].into(), + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + } + .into(), + } + .into(); println!( "Sending message of {msg_size} bytes while defragmentation buffer size is {MSG_DEFRAG_BUF} bytes" @@ -97,7 +109,7 @@ async fn run(endpoint: &EndPoint, channel: Channel, msg_size: usize) { // Wait on the router manager that the transport has been closed ztimeout!(async { - while !router_manager.get_transports_unicast().is_empty() { + while !router_manager.get_transports_unicast().await.is_empty() { task::sleep(SLEEP).await; } }); @@ -161,6 +173,7 @@ fn transport_unicast_defragmentation_tcp_only() { #[cfg(feature = "transport_ws")] #[test] +#[ignore] fn transport_unicast_defragmentation_ws_only() { let _ = env_logger::try_init(); task::block_on(async { @@ -195,3 +208,43 @@ fn transport_unicast_defragmentation_ws_only() { } }); } + +#[cfg(feature = "transport_shm")] +#[test] +#[ignore] +fn transport_unicast_defragmentation_shm_only() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + // Define the locators + let endpoint: EndPoint = "shm/transport_unicast_defragmentation_shm_only" + .parse() + .unwrap(); + // Define the reliability and congestion control + let channel = [ + Channel { + priority: Priority::default(), + reliability: Reliability::Reliable, + }, + Channel { + priority: Priority::default(), + reliability: Reliability::BestEffort, + }, + Channel { + priority: Priority::RealTime, + reliability: Reliability::Reliable, + }, + Channel { + priority: Priority::RealTime, + reliability: Reliability::BestEffort, + }, + ]; + // Run + task::block_on(async { + for ch in channel.iter() { + run(&endpoint, *ch, MSG_SIZE).await; + } + }); +} diff --git a/io/zenoh-transport/tests/unicast_intermittent.rs b/io/zenoh-transport/tests/unicast_intermittent.rs index db8e2f224f..81f7020d29 100644 --- a/io/zenoh-transport/tests/unicast_intermittent.rs +++ b/io/zenoh-transport/tests/unicast_intermittent.rs @@ -18,16 +18,22 @@ use std::convert::TryFrom; use std::io::Write; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; -use std::thread; use std::time::Duration; -use zenoh_buffers::ZBuf; use zenoh_core::zasync_executor_init; use zenoh_link::Link; use zenoh_protocol::{ - core::{Channel, CongestionControl, EndPoint, Priority, Reliability, WhatAmI, ZenohId}, - zenoh::ZenohMessage, + core::{CongestionControl, Encoding, EndPoint, Priority, WhatAmI, ZenohId}, + network::{ + push::{ + ext::{NodeIdType, QoSType}, + Push, + }, + NetworkMessage, + }, + zenoh::Put, }; use zenoh_result::ZResult; +use zenoh_transport::test_helpers::make_transport_manager_builder; use zenoh_transport::{ DummyTransportPeerEventHandler, TransportEventHandler, TransportManager, TransportMulticast, TransportMulticastEventHandler, TransportPeer, TransportPeerEventHandler, TransportUnicast, @@ -126,7 +132,7 @@ impl SCClient { } impl TransportPeerEventHandler for SCClient { - fn handle_message(&self, _message: ZenohMessage) -> ZResult<()> { + fn handle_message(&self, _message: NetworkMessage) -> ZResult<()> { self.counter.fetch_add(1, Ordering::AcqRel); Ok(()) } @@ -141,15 +147,22 @@ impl TransportPeerEventHandler for SCClient { } } -async fn transport_intermittent(endpoint: &EndPoint) { +async fn transport_intermittent( + endpoint: &EndPoint, + #[cfg(feature = "shared-memory")] shm_transport: bool, +) { /* [ROUTER] */ let router_id = ZenohId::try_from([1]).unwrap(); let router_handler = Arc::new(SHRouterIntermittent); // Create the router transport manager - let unicast = TransportManager::config_unicast() - .max_links(1) - .max_sessions(3); + let unicast = make_transport_manager_builder( + #[cfg(feature = "transport_multilink")] + 1, + #[cfg(feature = "shared-memory")] + shm_transport, + ) + .max_sessions(3); let router_manager = TransportManager::builder() .whatami(WhatAmI::Router) .zid(router_id) @@ -164,9 +177,13 @@ async fn transport_intermittent(endpoint: &EndPoint) { // Create the transport transport manager for the first client let counter = Arc::new(AtomicUsize::new(0)); - let unicast = TransportManager::config_unicast() - .max_links(1) - .max_sessions(3); + let unicast = make_transport_manager_builder( + #[cfg(feature = "transport_multilink")] + 1, + #[cfg(feature = "shared-memory")] + shm_transport, + ) + .max_sessions(3); let client01_manager = TransportManager::builder() .whatami(WhatAmI::Client) .zid(client01_id) @@ -175,9 +192,13 @@ async fn transport_intermittent(endpoint: &EndPoint) { .unwrap(); // Create the transport transport manager for the second client - let unicast = TransportManager::config_unicast() - .max_links(1) - .max_sessions(1); + let unicast = make_transport_manager_builder( + #[cfg(feature = "transport_multilink")] + 1, + #[cfg(feature = "shared-memory")] + shm_transport, + ) + .max_sessions(1); let client02_manager = TransportManager::builder() .whatami(WhatAmI::Client) .zid(client02_id) @@ -186,9 +207,13 @@ async fn transport_intermittent(endpoint: &EndPoint) { .unwrap(); // Create the transport transport manager for the third client - let unicast = TransportManager::config_unicast() - .max_links(1) - .max_sessions(1); + let unicast = make_transport_manager_builder( + #[cfg(feature = "transport_multilink")] + 1, + #[cfg(feature = "shared-memory")] + shm_transport, + ) + .max_sessions(1); let client03_manager = TransportManager::builder() .whatami(WhatAmI::Client) .zid(client03_id) @@ -206,9 +231,9 @@ async fn transport_intermittent(endpoint: &EndPoint) { /* [2] */ // Open a transport from client01 to the router - let c_ses1 = ztimeout!(client01_manager.open_transport(endpoint.clone())).unwrap(); + let c_ses1 = ztimeout!(client01_manager.open_transport_unicast(endpoint.clone())).unwrap(); assert_eq!(c_ses1.get_links().unwrap().len(), 1); - assert_eq!(client01_manager.get_transports().len(), 1); + assert_eq!(client01_manager.get_transports_unicast().await.len(), 1); assert_eq!(c_ses1.get_zid().unwrap(), router_id); /* [3] */ @@ -221,9 +246,10 @@ async fn transport_intermittent(endpoint: &EndPoint) { print!("+"); std::io::stdout().flush().unwrap(); - let c_ses2 = ztimeout!(c_client02_manager.open_transport(c_endpoint.clone())).unwrap(); + let c_ses2 = + ztimeout!(c_client02_manager.open_transport_unicast(c_endpoint.clone())).unwrap(); assert_eq!(c_ses2.get_links().unwrap().len(), 1); - assert_eq!(c_client02_manager.get_transports().len(), 1); + assert_eq!(c_client02_manager.get_transports_unicast().await.len(), 1); assert_eq!(c_ses2.get_zid().unwrap(), c_router_id); task::sleep(SLEEP).await; @@ -245,9 +271,10 @@ async fn transport_intermittent(endpoint: &EndPoint) { print!("*"); std::io::stdout().flush().unwrap(); - let c_ses3 = ztimeout!(c_client03_manager.open_transport(c_endpoint.clone())).unwrap(); + let c_ses3 = + ztimeout!(c_client03_manager.open_transport_unicast(c_endpoint.clone())).unwrap(); assert_eq!(c_ses3.get_links().unwrap().len(), 1); - assert_eq!(c_client03_manager.get_transports().len(), 1); + assert_eq!(c_client03_manager.get_transports_unicast().await.len(), 1); assert_eq!(c_ses3.get_zid().unwrap(), c_router_id); task::sleep(SLEEP).await; @@ -264,30 +291,25 @@ async fn transport_intermittent(endpoint: &EndPoint) { /* [4] */ println!("Transport Intermittent [4a1]"); let c_router_manager = router_manager.clone(); - ztimeout!(task::spawn_blocking(move || { + ztimeout!(task::spawn_blocking(move || task::block_on(async { // Create the message to send - let key = "test".into(); - let payload = ZBuf::from(vec![0_u8; MSG_SIZE]); - let channel = Channel { - priority: Priority::default(), - reliability: Reliability::Reliable, - }; - let congestion_control = CongestionControl::Block; - let data_info = None; - let routing_context = None; - let reply_context = None; - let attachment = None; - - let message = ZenohMessage::make_data( - key, - payload, - channel, - congestion_control, - data_info, - routing_context, - reply_context, - attachment, - ); + let message: NetworkMessage = Push { + wire_expr: "test".into(), + ext_qos: QoSType::new(Priority::default(), CongestionControl::Block, false), + ext_tstamp: None, + ext_nodeid: NodeIdType::default(), + payload: Put { + payload: vec![0u8; MSG_SIZE].into(), + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + } + .into(), + } + .into(); let mut ticks: Vec = (0..=MSG_COUNT).step_by(MSG_COUNT / 10).collect(); ticks.remove(0); @@ -298,7 +320,7 @@ async fn transport_intermittent(endpoint: &EndPoint) { println!("\nScheduled {count}"); ticks.remove(0); } - let transports = c_router_manager.get_transports(); + let transports = c_router_manager.get_transports_unicast().await; if !transports.is_empty() { for s in transports.iter() { if let Ok(ll) = s.get_links() { @@ -317,10 +339,10 @@ async fn transport_intermittent(endpoint: &EndPoint) { count += 1; } else { print!("O"); - thread::sleep(USLEEP); + task::sleep(USLEEP).await; } } - })); + }))); // Stop the tasks ztimeout!(c2_handle.cancel()); @@ -342,15 +364,15 @@ async fn transport_intermittent(endpoint: &EndPoint) { /* [5] */ // Close the open transport on the client println!("Transport Intermittent [5a1]"); - for s in client01_manager.get_transports().iter() { + for s in client01_manager.get_transports_unicast().await.iter() { ztimeout!(s.close()).unwrap(); } println!("Transport Intermittent [5a2]"); - for s in client02_manager.get_transports().iter() { + for s in client02_manager.get_transports_unicast().await.iter() { ztimeout!(s.close()).unwrap(); } println!("Transport Intermittent [5a3]"); - for s in client03_manager.get_transports().iter() { + for s in client03_manager.get_transports_unicast().await.iter() { ztimeout!(s.close()).unwrap(); } @@ -359,7 +381,7 @@ async fn transport_intermittent(endpoint: &EndPoint) { println!("Transport Intermittent [6a1]"); ztimeout!(async { loop { - let transports = router_manager.get_transports(); + let transports = router_manager.get_transports_unicast().await; if transports.is_empty() { break; } @@ -384,6 +406,20 @@ async fn transport_intermittent(endpoint: &EndPoint) { task::sleep(SLEEP).await; } +async fn net_transport_intermittent(endpoint: &EndPoint) { + transport_intermittent( + endpoint, + #[cfg(feature = "shared-memory")] + false, + ) + .await +} + +#[cfg(feature = "shared-memory")] +async fn shm_transport_intermittent(endpoint: &EndPoint) { + transport_intermittent(endpoint, true).await +} + #[cfg(feature = "transport_tcp")] #[test] fn transport_tcp_intermittent() { @@ -393,11 +429,24 @@ fn transport_tcp_intermittent() { }); let endpoint: EndPoint = format!("tcp/127.0.0.1:{}", 12000).parse().unwrap(); - task::block_on(transport_intermittent(&endpoint)); + task::block_on(net_transport_intermittent(&endpoint)); +} + +#[cfg(all(feature = "transport_tcp", feature = "shared-memory"))] +#[test] +fn transport_tcp_intermittent_for_shm_transport() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + let endpoint: EndPoint = format!("tcp/127.0.0.1:{}", 12100).parse().unwrap(); + task::block_on(shm_transport_intermittent(&endpoint)); } #[cfg(feature = "transport_ws")] #[test] +#[ignore] fn transport_ws_intermittent() { let _ = env_logger::try_init(); task::block_on(async { @@ -405,5 +454,46 @@ fn transport_ws_intermittent() { }); let endpoint: EndPoint = format!("ws/127.0.0.1:{}", 12010).parse().unwrap(); - task::block_on(transport_intermittent(&endpoint)); + task::block_on(net_transport_intermittent(&endpoint)); +} + +#[cfg(all(feature = "transport_ws", feature = "shared-memory"))] +#[test] +#[ignore] +fn transport_ws_intermittent_for_shm_transport() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + let endpoint: EndPoint = format!("ws/127.0.0.1:{}", 12110).parse().unwrap(); + task::block_on(shm_transport_intermittent(&endpoint)); +} + +#[cfg(feature = "transport_shm")] +#[test] +#[ignore] +fn transport_shm_intermittent() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + let endpoint: EndPoint = "shm/transport_shm_intermittent".parse().unwrap(); + task::block_on(net_transport_intermittent(&endpoint)); +} + +#[cfg(all(feature = "transport_shm", feature = "shared-memory"))] +#[test] +#[ignore] +fn transport_shm_intermittent_for_shm_transport() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + let endpoint: EndPoint = "shm/transport_shm_intermittent_for_shm_transport" + .parse() + .unwrap(); + task::block_on(shm_transport_intermittent(&endpoint)); } diff --git a/io/zenoh-transport/tests/unicast_multilink.rs b/io/zenoh-transport/tests/unicast_multilink.rs new file mode 100644 index 0000000000..fcfb6cd492 --- /dev/null +++ b/io/zenoh-transport/tests/unicast_multilink.rs @@ -0,0 +1,754 @@ +// +// Copyright (c) 2022 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +#[cfg(feature = "transport_multilink")] +mod tests { + use async_std::{prelude::FutureExt, task}; + use std::{convert::TryFrom, sync::Arc, time::Duration}; + use zenoh_core::zasync_executor_init; + use zenoh_link::EndPoint; + use zenoh_protocol::core::{WhatAmI, ZenohId}; + use zenoh_result::ZResult; + use zenoh_transport::{ + DummyTransportPeerEventHandler, TransportEventHandler, TransportManager, + TransportMulticast, TransportMulticastEventHandler, TransportPeer, + TransportPeerEventHandler, TransportUnicast, + }; + + const TIMEOUT: Duration = Duration::from_secs(60); + const SLEEP: Duration = Duration::from_millis(100); + + macro_rules! ztimeout { + ($f:expr) => { + $f.timeout(TIMEOUT).await.unwrap() + }; + } + + #[cfg(test)] + #[derive(Default)] + struct SHRouterOpenClose; + + impl TransportEventHandler for SHRouterOpenClose { + fn new_unicast( + &self, + _peer: TransportPeer, + _transport: TransportUnicast, + ) -> ZResult> { + Ok(Arc::new(DummyTransportPeerEventHandler)) + } + + fn new_multicast( + &self, + _transport: TransportMulticast, + ) -> ZResult> { + panic!(); + } + } + + // Transport Handler for the client + struct SHClientOpenClose {} + + impl SHClientOpenClose { + fn new() -> Self { + Self {} + } + } + + impl TransportEventHandler for SHClientOpenClose { + fn new_unicast( + &self, + _peer: TransportPeer, + _transport: TransportUnicast, + ) -> ZResult> { + Ok(Arc::new(DummyTransportPeerEventHandler)) + } + + fn new_multicast( + &self, + _transport: TransportMulticast, + ) -> ZResult> { + panic!(); + } + } + + async fn multilink_transport(endpoint: &EndPoint) { + /* [ROUTER] */ + let router_id = ZenohId::try_from([1]).unwrap(); + + let router_handler = Arc::new(SHRouterOpenClose); + // Create the router transport manager + let unicast = TransportManager::config_unicast() + .max_links(2) + .max_sessions(2); + let router_manager = TransportManager::builder() + .whatami(WhatAmI::Router) + .zid(router_id) + .unicast(unicast) + .build(router_handler.clone()) + .unwrap(); + + /* [CLIENT] */ + let client01_id = ZenohId::try_from([2]).unwrap(); + let client02_id = ZenohId::try_from([3]).unwrap(); + + // Create the transport transport manager for the first client + let unicast = TransportManager::config_unicast() + .max_links(2) + .max_sessions(1); + let client01_manager = TransportManager::builder() + .whatami(WhatAmI::Client) + .zid(client01_id) + .unicast(unicast) + .build(Arc::new(SHClientOpenClose::new())) + .unwrap(); + + // Create the transport transport manager for the second client + let unicast = TransportManager::config_unicast() + .max_links(1) + .max_sessions(1); + let client02_manager = TransportManager::builder() + .whatami(WhatAmI::Client) + .zid(client02_id) + .unicast(unicast) + .build(Arc::new(SHClientOpenClose::new())) + .unwrap(); + + // Create the transport transport manager for the third client spoofing the first + let unicast = TransportManager::config_unicast() + .max_links(2) + .max_sessions(1); + let client03_manager = TransportManager::builder() + .whatami(WhatAmI::Client) + .zid(client01_id) + .unicast(unicast) + .build(Arc::new(SHClientOpenClose::new())) + .unwrap(); + + /* [1] */ + println!("\nTransport Open Close [1a1]"); + // Add the locator on the router + let res = ztimeout!(router_manager.add_listener(endpoint.clone())); + println!("Transport Open Close [1a1]: {res:?}"); + assert!(res.is_ok()); + println!("Transport Open Close [1a2]"); + let locators = router_manager.get_listeners(); + println!("Transport Open Close [1a2]: {locators:?}"); + assert_eq!(locators.len(), 1); + + // Open a first transport from the client to the router + // -> This should be accepted + let mut links_num = 1; + + println!("Transport Open Close [1c1]"); + let res = ztimeout!(client01_manager.open_transport_unicast(endpoint.clone())); + println!("Transport Open Close [1c2]: {res:?}"); + assert!(res.is_ok()); + let c_ses1 = res.unwrap(); + println!("Transport Open Close [1d1]"); + let transports = client01_manager.get_transports_unicast().await; + println!("Transport Open Close [1d2]: {transports:?}"); + assert_eq!(transports.len(), 1); + assert_eq!(c_ses1.get_zid().unwrap(), router_id); + println!("Transport Open Close [1e1]"); + let links = c_ses1.get_links().unwrap(); + println!("Transport Open Close [1e2]: {links:?}"); + assert_eq!(links.len(), links_num); + + // Verify that the transport has been open on the router + println!("Transport Open Close [1f1]"); + ztimeout!(async { + loop { + let transports = router_manager.get_transports_unicast().await; + let s = transports + .iter() + .find(|s| s.get_zid().unwrap() == client01_id); + + match s { + Some(s) => { + let links = s.get_links().unwrap(); + assert_eq!(links.len(), links_num); + break; + } + None => task::sleep(SLEEP).await, + } + } + }); + + /* [2] */ + // Open a second transport from the client to the router + // -> This should be accepted + links_num = 2; + + println!("\nTransport Open Close [2a1]"); + let res = ztimeout!(client01_manager.open_transport_unicast(endpoint.clone())); + println!("Transport Open Close [2a2]: {res:?}"); + assert!(res.is_ok()); + let c_ses2 = res.unwrap(); + println!("Transport Open Close [2b1]"); + let transports = client01_manager.get_transports_unicast().await; + println!("Transport Open Close [2b2]: {transports:?}"); + assert_eq!(transports.len(), 1); + assert_eq!(c_ses2.get_zid().unwrap(), router_id); + println!("Transport Open Close [2c1]"); + let links = c_ses2.get_links().unwrap(); + println!("Transport Open Close [2c2]: {links:?}"); + assert_eq!(links.len(), links_num); + assert_eq!(c_ses2, c_ses1); + + // Verify that the transport has been open on the router + println!("Transport Open Close [2d1]"); + ztimeout!(async { + loop { + let transports = router_manager.get_transports_unicast().await; + let s = transports + .iter() + .find(|s| s.get_zid().unwrap() == client01_id) + .unwrap(); + + let links = s.get_links().unwrap(); + if links.len() == links_num { + break; + } + task::sleep(SLEEP).await; + } + }); + + /* [3] */ + // Open transport -> This should be rejected because + // of the maximum limit of links per transport + println!("\nTransport Open Close [3a1]"); + let res = ztimeout!(client01_manager.open_transport_unicast(endpoint.clone())); + println!("Transport Open Close [3a2]: {res:?}"); + assert!(res.is_err()); + println!("Transport Open Close [3b1]"); + let transports = client01_manager.get_transports_unicast().await; + println!("Transport Open Close [3b2]: {transports:?}"); + assert_eq!(transports.len(), 1); + assert_eq!(c_ses1.get_zid().unwrap(), router_id); + println!("Transport Open Close [3c1]"); + let links = c_ses1.get_links().unwrap(); + println!("Transport Open Close [3c2]: {links:?}"); + assert_eq!(links.len(), links_num); + + // Verify that the transport has not been open on the router + println!("Transport Open Close [3d1]"); + ztimeout!(async { + task::sleep(SLEEP).await; + let transports = router_manager.get_transports_unicast().await; + assert_eq!(transports.len(), 1); + let s = transports + .iter() + .find(|s| s.get_zid().unwrap() == client01_id) + .unwrap(); + let links = s.get_links().unwrap(); + assert_eq!(links.len(), links_num); + }); + + /* [4] */ + // Close the open transport on the client + println!("\nTransport Open Close [4a1]"); + let res = ztimeout!(c_ses1.close()); + println!("Transport Open Close [4a2]: {res:?}"); + assert!(res.is_ok()); + println!("Transport Open Close [4b1]"); + let transports = client01_manager.get_transports_unicast().await; + println!("Transport Open Close [4b2]: {transports:?}"); + assert_eq!(transports.len(), 0); + + // Verify that the transport has been closed also on the router + println!("Transport Open Close [4c1]"); + ztimeout!(async { + loop { + let transports = router_manager.get_transports_unicast().await; + let index = transports + .iter() + .find(|s| s.get_zid().unwrap() == client01_id); + if index.is_none() { + break; + } + task::sleep(SLEEP).await; + } + }); + + /* [5] */ + // Open transport -> This should be accepted because + // the number of links should be back to 0 + links_num = 1; + + println!("\nTransport Open Close [5a1]"); + let res = ztimeout!(client01_manager.open_transport_unicast(endpoint.clone())); + println!("Transport Open Close [5a2]: {res:?}"); + assert!(res.is_ok()); + let c_ses3 = res.unwrap(); + println!("Transport Open Close [5b1]"); + let transports = client01_manager.get_transports_unicast().await; + println!("Transport Open Close [5b2]: {transports:?}"); + assert_eq!(transports.len(), 1); + assert_eq!(c_ses3.get_zid().unwrap(), router_id); + println!("Transport Open Close [5c1]"); + let links = c_ses3.get_links().unwrap(); + println!("Transport Open Close [5c2]: {links:?}"); + assert_eq!(links.len(), links_num); + + // Verify that the transport has been open on the router + println!("Transport Open Close [5d1]"); + ztimeout!(async { + task::sleep(SLEEP).await; + let transports = router_manager.get_transports_unicast().await; + assert_eq!(transports.len(), 1); + let s = transports + .iter() + .find(|s| s.get_zid().unwrap() == client01_id) + .unwrap(); + let links = s.get_links().unwrap(); + assert_eq!(links.len(), links_num); + }); + + /* [6] */ + // Open transport -> This should be rejected because + // of the maximum limit of transports + println!("\nTransport Open Close [6a1]"); + let res = ztimeout!(client02_manager.open_transport_unicast(endpoint.clone())); + println!("Transport Open Close [6a2]: {res:?}"); + assert!(res.is_ok()); + let c_ses4 = res.unwrap(); + println!("Transport Open Close [6b1]"); + let transports = client02_manager.get_transports_unicast().await; + println!("Transport Open Close [6b2]: {transports:?}"); + assert_eq!(transports.len(), 1); + assert_eq!(c_ses4.get_zid().unwrap(), router_id); + println!("Transport Open Close [6c1]"); + let links = c_ses4.get_links().unwrap(); + println!("Transport Open Close [6c2]: {links:?}"); + assert_eq!(links.len(), links_num); + + // Open transport -> This should be rejected because + // of the maximum limit of transports + println!("\nTransport Open Close [6d1]"); + let res = ztimeout!(client02_manager.open_transport_unicast(endpoint.clone())); + println!("Transport Open Close [6d2]: {res:?}"); + assert!(res.is_err()); + println!("Transport Open Close [6e1]"); + let transports = client02_manager.get_transports_unicast().await; + println!("Transport Open Close [6e2]: {transports:?}"); + assert_eq!(transports.len(), 1); + + // Verify that the transport has been open on the router + println!("Transport Open Close [6f1]"); + ztimeout!(async { + task::sleep(SLEEP).await; + let transports = router_manager.get_transports_unicast().await; + assert_eq!(transports.len(), 2); + let s = transports + .iter() + .find(|s| s.get_zid().unwrap() == client01_id) + .unwrap(); + let links = s.get_links().unwrap(); + assert_eq!(links.len(), links_num); + }); + + /* [7] */ + // Try to spoof the first client + // -> This should be rejected + println!("\nTransport Open Close [7a1]"); + let res = ztimeout!(client03_manager.open_transport_unicast(endpoint.clone())); + println!("Transport Open Close [7a2]: {res:?}"); + assert!(res.is_err()); + println!("Transport Open Close [7b1]"); + let transports = client03_manager.get_transports_unicast().await; + println!("Transport Open Close [7b2]: {transports:?}"); + assert_eq!(transports.len(), 0); + + /* [8] */ + // Close the open transport on the client + println!("\nTransport Open Close [8a1]"); + let res = ztimeout!(c_ses3.close()); + println!("Transport Open Close [8a2]: {res:?}"); + assert!(res.is_ok()); + println!("\nTransport Open Close [8b1]"); + let res = ztimeout!(c_ses4.close()); + println!("Transport Open Close [8b2]: {res:?}"); + assert!(res.is_ok()); + println!("Transport Open Close [8c1]"); + let transports = client01_manager.get_transports_unicast().await; + println!("Transport Open Close [8c2]: {transports:?}"); + assert_eq!(transports.len(), 0); + + // Verify that the transport has been closed also on the router + println!("Transport Open Close [8d1]"); + ztimeout!(async { + loop { + let transports = router_manager.get_transports_unicast().await; + if transports.is_empty() { + break; + } + task::sleep(SLEEP).await; + } + }); + + /* [9] */ + // Open transport -> This should be accepted because + // the number of transports should be back to 0 + links_num = 1; + + println!("\nTransport Open Close [9a1]"); + let res = ztimeout!(client02_manager.open_transport_unicast(endpoint.clone())); + println!("Transport Open Close [9a2]: {res:?}"); + assert!(res.is_ok()); + let c_ses4 = res.unwrap(); + println!("Transport Open Close [9b1]"); + let transports = client02_manager.get_transports_unicast().await; + println!("Transport Open Close [9b2]: {transports:?}"); + assert_eq!(transports.len(), 1); + println!("Transport Open Close [9c1]"); + let links = c_ses4.get_links().unwrap(); + println!("Transport Open Close [9c2]: {links:?}"); + assert_eq!(links.len(), links_num); + + // Verify that the transport has been open on the router + println!("Transport Open Close [9d1]"); + ztimeout!(async { + loop { + let transports = router_manager.get_transports_unicast().await; + let s = transports + .iter() + .find(|s| s.get_zid().unwrap() == client02_id); + match s { + Some(s) => { + let links = s.get_links().unwrap(); + assert_eq!(links.len(), links_num); + break; + } + None => task::sleep(SLEEP).await, + } + } + }); + + /* [9] */ + // Close the open transport on the client + println!("Transport Open Close [9a1]"); + let res = ztimeout!(c_ses4.close()); + println!("Transport Open Close [9a2]: {res:?}"); + assert!(res.is_ok()); + println!("Transport Open Close [9b1]"); + let transports = client02_manager.get_transports_unicast().await; + println!("Transport Open Close [9b2]: {transports:?}"); + assert_eq!(transports.len(), 0); + + // Verify that the transport has been closed also on the router + println!("Transport Open Close [9c1]"); + ztimeout!(async { + loop { + let transports = router_manager.get_transports_unicast().await; + if transports.is_empty() { + break; + } + task::sleep(SLEEP).await; + } + }); + + /* [10] */ + // Perform clean up of the open locators + println!("\nTransport Open Close [10a1]"); + let res = ztimeout!(router_manager.del_listener(endpoint)); + println!("Transport Open Close [10a2]: {res:?}"); + assert!(res.is_ok()); + + ztimeout!(async { + while !router_manager.get_listeners().is_empty() { + task::sleep(SLEEP).await; + } + }); + + // Wait a little bit + task::sleep(SLEEP).await; + + ztimeout!(router_manager.close()); + ztimeout!(client01_manager.close()); + ztimeout!(client02_manager.close()); + + // Wait a little bit + task::sleep(SLEEP).await; + } + + #[cfg(feature = "transport_tcp")] + #[test] + fn multilink_tcp_only() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + let endpoint: EndPoint = format!("tcp/127.0.0.1:{}", 18000).parse().unwrap(); + task::block_on(multilink_transport(&endpoint)); + } + + #[cfg(feature = "transport_udp")] + #[test] + fn multilink_udp_only() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + let endpoint: EndPoint = format!("udp/127.0.0.1:{}", 18010).parse().unwrap(); + task::block_on(multilink_transport(&endpoint)); + } + + #[cfg(feature = "transport_ws")] + #[test] + #[ignore] + fn multilink_ws_only() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + let endpoint: EndPoint = format!("ws/127.0.0.1:{}", 18020).parse().unwrap(); + task::block_on(multilink_transport(&endpoint)); + } + + #[cfg(feature = "transport_shm")] + #[test] + #[ignore] + fn multilink_shm_only() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + let endpoint: EndPoint = "shm/multilink_shm_only".parse().unwrap(); + task::block_on(multilink_transport(&endpoint)); + } + + #[cfg(all(feature = "transport_unixsock-stream", target_family = "unix"))] + #[test] + #[ignore] + fn multilink_unix_only() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + let f1 = "zenoh-test-unix-socket-9.sock"; + let _ = std::fs::remove_file(f1); + let endpoint: EndPoint = format!("unixsock-stream/{f1}").parse().unwrap(); + task::block_on(multilink_transport(&endpoint)); + let _ = std::fs::remove_file(f1); + let _ = std::fs::remove_file(format!("{f1}.lock")); + } + + #[cfg(feature = "transport_tls")] + #[test] + fn multilink_tls_only() { + use zenoh_link::tls::config::*; + + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + // NOTE: this an auto-generated pair of certificate and key. + // The target domain is localhost, so it has no real + // mapping to any existing domain. The certificate and key + // have been generated using: https://github.com/jsha/minica + let key = "-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAsfqAuhElN4HnyeqLovSd4Qe+nNv5AwCjSO+HFiF30x3vQ1Hi +qRA0UmyFlSqBnFH3TUHm4Jcad40QfrX8f11NKGZdpvKHsMYqYjZnYkRFGS2s4fQy +aDbV5M06s3UDX8ETPgY41Y8fCKTSVdi9iHkwcVrXMxUu4IBBx0C1r2GSo3gkIBnU +cELdFdaUOSbdCipJhbnkwixEr2h7PXxwba7SIZgZtRaQWak1VE9b716qe3iMuMha +Efo/UoFmeZCPu5spfwaOZsnCsxRPk2IjbzlsHTJ09lM9wmbEFHBMVAXejLTk++Sr +Xt8jASZhNen/2GzyLQNAquGn98lCMQ6SsE9vLQIDAQABAoIBAGQkKggHm6Q20L+4 +2+bNsoOqguLplpvM4RMpyx11qWE9h6GeUmWD+5yg+SysJQ9aw0ZSHWEjRD4ePji9 +lxvm2IIxzuIftp+NcM2gBN2ywhpfq9XbO/2NVR6PJ0dQQJzBG12bzKDFDdYkP0EU +WdiPL+WoEkvo0F57bAd77n6G7SZSgxYekBF+5S6rjbu5I1cEKW+r2vLehD4uFCVX +Q0Tu7TyIOE1KJ2anRb7ZXVUaguNj0/Er7EDT1+wN8KJKvQ1tYGIq/UUBtkP9nkOI +9XJd25k6m5AQPDddzd4W6/5+M7kjyVPi3CsQcpBPss6ueyecZOMaKqdWAHeEyaak +r67TofUCgYEA6GBa+YkRvp0Ept8cd5mh4gCRM8wUuhtzTQnhubCPivy/QqMWScdn +qD0OiARLAsqeoIfkAVgyqebVnxwTrKTvWe0JwpGylEVWQtpGz3oHgjST47yZxIiY +CSAaimi2CYnJZ+QB2oBkFVwNCuXdPEGX6LgnOGva19UKrm6ONsy6V9MCgYEAxBJu +fu4dGXZreARKEHa/7SQjI9ayAFuACFlON/EgSlICzQyG/pumv1FsMEiFrv6w7PRj +4AGqzyzGKXWVDRMrUNVeGPSKJSmlPGNqXfPaXRpVEeB7UQhAs5wyMrWDl8jEW7Ih +XcWhMLn1f/NOAKyrSDSEaEM+Nuu+xTifoAghvP8CgYEAlta9Fw+nihDIjT10cBo0 +38w4dOP7bFcXQCGy+WMnujOYPzw34opiue1wOlB3FIfL8i5jjY/fyzPA5PhHuSCT +Ec9xL3B9+AsOFHU108XFi/pvKTwqoE1+SyYgtEmGKKjdKOfzYA9JaCgJe1J8inmV +jwXCx7gTJVjwBwxSmjXIm+sCgYBQF8NhQD1M0G3YCdCDZy7BXRippCL0OGxVfL2R +5oKtOVEBl9NxH/3+evE5y/Yn5Mw7Dx3ZPHUcygpslyZ6v9Da5T3Z7dKcmaVwxJ+H +n3wcugv0EIHvOPLNK8npovINR6rGVj6BAqD0uZHKYYYEioQxK5rGyGkaoDQ+dgHm +qku12wKBgQDem5FvNp5iW7mufkPZMqf3sEGtu612QeqejIPFM1z7VkUgetsgPBXD +tYsqC2FtWzY51VOEKNpnfH7zH5n+bjoI9nAEAW63TK9ZKkr2hRGsDhJdGzmLfQ7v +F6/CuIw9EsAq6qIB8O88FXQqald+BZOx6AzB8Oedsz/WtMmIEmr/+Q== +-----END RSA PRIVATE KEY-----"; + + let cert = "-----BEGIN CERTIFICATE----- +MIIDLjCCAhagAwIBAgIIeUtmIdFQznMwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE +AxMVbWluaWNhIHJvb3QgY2EgMDc4ZGE3MCAXDTIzMDMwNjE2MDMxOFoYDzIxMjMw +MzA2MTYwMzE4WjAUMRIwEAYDVQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQCx+oC6ESU3gefJ6oui9J3hB76c2/kDAKNI74cWIXfT +He9DUeKpEDRSbIWVKoGcUfdNQebglxp3jRB+tfx/XU0oZl2m8oewxipiNmdiREUZ +Lazh9DJoNtXkzTqzdQNfwRM+BjjVjx8IpNJV2L2IeTBxWtczFS7ggEHHQLWvYZKj +eCQgGdRwQt0V1pQ5Jt0KKkmFueTCLESvaHs9fHBtrtIhmBm1FpBZqTVUT1vvXqp7 +eIy4yFoR+j9SgWZ5kI+7myl/Bo5mycKzFE+TYiNvOWwdMnT2Uz3CZsQUcExUBd6M +tOT75Kte3yMBJmE16f/YbPItA0Cq4af3yUIxDpKwT28tAgMBAAGjdjB0MA4GA1Ud +DwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0T +AQH/BAIwADAfBgNVHSMEGDAWgBTWfAmQ/BUIQm/9/llJJs2jUMWzGzAUBgNVHREE +DTALgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggEBAG/POnBob0S7iYwsbtI2 +3LTTbRnmseIErtJuJmI9yYzgVIm6sUSKhlIUfAIm4rfRuzE94KFeWR2w9RabxOJD +wjYLLKvQ6rFY5g2AV/J0TwDjYuq0absdaDPZ8MKJ+/lpGYK3Te+CTOfq5FJRFt1q +GOkXAxnNpGg0obeRWRKFiAMHbcw6a8LIMfRjCooo3+uSQGsbVzGxSB4CYo720KcC +9vB1K9XALwzoqCewP4aiQsMY1GWpAmzXJftY3w+lka0e9dBYcdEdOqxSoZb5OBBZ +p5e60QweRuJsb60aUaCG8HoICevXYK2fFqCQdlb5sIqQqXyN2K6HuKAFywsjsGyJ +abY= +-----END CERTIFICATE-----"; + + // Configure the client + let ca = "-----BEGIN CERTIFICATE----- +MIIDSzCCAjOgAwIBAgIIB42n1ZIkOakwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE +AxMVbWluaWNhIHJvb3QgY2EgMDc4ZGE3MCAXDTIzMDMwNjE2MDMwN1oYDzIxMjMw +MzA2MTYwMzA3WjAgMR4wHAYDVQQDExVtaW5pY2Egcm9vdCBjYSAwNzhkYTcwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIuCq24O4P4Aep5vAVlrIQ7P8+ +uWWgcHIFYa02TmhBUB/hjo0JANCQvAtpVNuQ8NyKPlqnnq1cttePbSYVeA0rrnOs +DcfySAiyGBEY9zMjFfHJtH1wtrPcJEU8XIEY3xUlrAJE2CEuV9dVYgfEEydnvgLc +8Ug0WXSiARjqbnMW3l8jh6bYCp/UpL/gSM4mxdKrgpfyPoweGhlOWXc3RTS7cqM9 +T25acURGOSI6/g8GF0sNE4VZmUvHggSTmsbLeXMJzxDWO+xVehRmbQx3IkG7u++b +QdRwGIJcDNn7zHlDMHtQ0Z1DBV94fZNBwCULhCBB5g20XTGw//S7Fj2FPwyhAgMB +AAGjgYYwgYMwDgYDVR0PAQH/BAQDAgKEMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr +BgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBTWfAmQ/BUIQm/9 +/llJJs2jUMWzGzAfBgNVHSMEGDAWgBTWfAmQ/BUIQm/9/llJJs2jUMWzGzANBgkq +hkiG9w0BAQsFAAOCAQEAvtcZFAELKiTuOiAeYts6zeKxc+nnHCzayDeD/BDCbxGJ +e1n+xdHjLtWGd+/Anc+fvftSYBPTFQqCi84lPiUIln5z/rUxE+ke81hNPIfw2obc +yIg87xCabQpVyEh8s+MV+7YPQ1+fH4FuSi2Fck1FejxkVqN2uOZPvOYUmSTsaVr1 +8SfRnwJNZ9UMRPM2bD4Jkvj0VcL42JM3QkOClOzYW4j/vll2cSs4kx7er27cIoo1 +Ck0v2xSPAiVjg6w65rUQeW6uB5m0T2wyj+wm0At8vzhZPlgS1fKhcmT2dzOq3+oN +R+IdLiXcyIkg0m9N8I17p0ljCSkbrgGMD3bbePRTfg== +-----END CERTIFICATE-----"; + + let mut endpoint: EndPoint = format!("tls/localhost:{}", 18030).parse().unwrap(); + endpoint + .config_mut() + .extend( + [ + (TLS_ROOT_CA_CERTIFICATE_RAW, ca), + (TLS_SERVER_PRIVATE_KEY_RAW, key), + (TLS_SERVER_CERTIFICATE_RAW, cert), + ] + .iter() + .map(|(k, v)| ((*k).to_owned(), (*v).to_owned())), + ) + .unwrap(); + + task::block_on(multilink_transport(&endpoint)); + } + + #[cfg(feature = "transport_quic")] + #[test] + fn multilink_quic_only() { + use zenoh_link::quic::config::*; + + task::block_on(async { + zasync_executor_init!(); + }); + + // NOTE: this an auto-generated pair of certificate and key. + // The target domain is localhost, so it has no real + // mapping to any existing domain. The certificate and key + // have been generated using: https://github.com/jsha/minica + let key = "-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAsfqAuhElN4HnyeqLovSd4Qe+nNv5AwCjSO+HFiF30x3vQ1Hi +qRA0UmyFlSqBnFH3TUHm4Jcad40QfrX8f11NKGZdpvKHsMYqYjZnYkRFGS2s4fQy +aDbV5M06s3UDX8ETPgY41Y8fCKTSVdi9iHkwcVrXMxUu4IBBx0C1r2GSo3gkIBnU +cELdFdaUOSbdCipJhbnkwixEr2h7PXxwba7SIZgZtRaQWak1VE9b716qe3iMuMha +Efo/UoFmeZCPu5spfwaOZsnCsxRPk2IjbzlsHTJ09lM9wmbEFHBMVAXejLTk++Sr +Xt8jASZhNen/2GzyLQNAquGn98lCMQ6SsE9vLQIDAQABAoIBAGQkKggHm6Q20L+4 +2+bNsoOqguLplpvM4RMpyx11qWE9h6GeUmWD+5yg+SysJQ9aw0ZSHWEjRD4ePji9 +lxvm2IIxzuIftp+NcM2gBN2ywhpfq9XbO/2NVR6PJ0dQQJzBG12bzKDFDdYkP0EU +WdiPL+WoEkvo0F57bAd77n6G7SZSgxYekBF+5S6rjbu5I1cEKW+r2vLehD4uFCVX +Q0Tu7TyIOE1KJ2anRb7ZXVUaguNj0/Er7EDT1+wN8KJKvQ1tYGIq/UUBtkP9nkOI +9XJd25k6m5AQPDddzd4W6/5+M7kjyVPi3CsQcpBPss6ueyecZOMaKqdWAHeEyaak +r67TofUCgYEA6GBa+YkRvp0Ept8cd5mh4gCRM8wUuhtzTQnhubCPivy/QqMWScdn +qD0OiARLAsqeoIfkAVgyqebVnxwTrKTvWe0JwpGylEVWQtpGz3oHgjST47yZxIiY +CSAaimi2CYnJZ+QB2oBkFVwNCuXdPEGX6LgnOGva19UKrm6ONsy6V9MCgYEAxBJu +fu4dGXZreARKEHa/7SQjI9ayAFuACFlON/EgSlICzQyG/pumv1FsMEiFrv6w7PRj +4AGqzyzGKXWVDRMrUNVeGPSKJSmlPGNqXfPaXRpVEeB7UQhAs5wyMrWDl8jEW7Ih +XcWhMLn1f/NOAKyrSDSEaEM+Nuu+xTifoAghvP8CgYEAlta9Fw+nihDIjT10cBo0 +38w4dOP7bFcXQCGy+WMnujOYPzw34opiue1wOlB3FIfL8i5jjY/fyzPA5PhHuSCT +Ec9xL3B9+AsOFHU108XFi/pvKTwqoE1+SyYgtEmGKKjdKOfzYA9JaCgJe1J8inmV +jwXCx7gTJVjwBwxSmjXIm+sCgYBQF8NhQD1M0G3YCdCDZy7BXRippCL0OGxVfL2R +5oKtOVEBl9NxH/3+evE5y/Yn5Mw7Dx3ZPHUcygpslyZ6v9Da5T3Z7dKcmaVwxJ+H +n3wcugv0EIHvOPLNK8npovINR6rGVj6BAqD0uZHKYYYEioQxK5rGyGkaoDQ+dgHm +qku12wKBgQDem5FvNp5iW7mufkPZMqf3sEGtu612QeqejIPFM1z7VkUgetsgPBXD +tYsqC2FtWzY51VOEKNpnfH7zH5n+bjoI9nAEAW63TK9ZKkr2hRGsDhJdGzmLfQ7v +F6/CuIw9EsAq6qIB8O88FXQqald+BZOx6AzB8Oedsz/WtMmIEmr/+Q== +-----END RSA PRIVATE KEY-----"; + + let cert = "-----BEGIN CERTIFICATE----- +MIIDLjCCAhagAwIBAgIIeUtmIdFQznMwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE +AxMVbWluaWNhIHJvb3QgY2EgMDc4ZGE3MCAXDTIzMDMwNjE2MDMxOFoYDzIxMjMw +MzA2MTYwMzE4WjAUMRIwEAYDVQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQCx+oC6ESU3gefJ6oui9J3hB76c2/kDAKNI74cWIXfT +He9DUeKpEDRSbIWVKoGcUfdNQebglxp3jRB+tfx/XU0oZl2m8oewxipiNmdiREUZ +Lazh9DJoNtXkzTqzdQNfwRM+BjjVjx8IpNJV2L2IeTBxWtczFS7ggEHHQLWvYZKj +eCQgGdRwQt0V1pQ5Jt0KKkmFueTCLESvaHs9fHBtrtIhmBm1FpBZqTVUT1vvXqp7 +eIy4yFoR+j9SgWZ5kI+7myl/Bo5mycKzFE+TYiNvOWwdMnT2Uz3CZsQUcExUBd6M +tOT75Kte3yMBJmE16f/YbPItA0Cq4af3yUIxDpKwT28tAgMBAAGjdjB0MA4GA1Ud +DwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0T +AQH/BAIwADAfBgNVHSMEGDAWgBTWfAmQ/BUIQm/9/llJJs2jUMWzGzAUBgNVHREE +DTALgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggEBAG/POnBob0S7iYwsbtI2 +3LTTbRnmseIErtJuJmI9yYzgVIm6sUSKhlIUfAIm4rfRuzE94KFeWR2w9RabxOJD +wjYLLKvQ6rFY5g2AV/J0TwDjYuq0absdaDPZ8MKJ+/lpGYK3Te+CTOfq5FJRFt1q +GOkXAxnNpGg0obeRWRKFiAMHbcw6a8LIMfRjCooo3+uSQGsbVzGxSB4CYo720KcC +9vB1K9XALwzoqCewP4aiQsMY1GWpAmzXJftY3w+lka0e9dBYcdEdOqxSoZb5OBBZ +p5e60QweRuJsb60aUaCG8HoICevXYK2fFqCQdlb5sIqQqXyN2K6HuKAFywsjsGyJ +abY= +-----END CERTIFICATE-----"; + + // Configure the client + let ca = "-----BEGIN CERTIFICATE----- +MIIDSzCCAjOgAwIBAgIIB42n1ZIkOakwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE +AxMVbWluaWNhIHJvb3QgY2EgMDc4ZGE3MCAXDTIzMDMwNjE2MDMwN1oYDzIxMjMw +MzA2MTYwMzA3WjAgMR4wHAYDVQQDExVtaW5pY2Egcm9vdCBjYSAwNzhkYTcwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIuCq24O4P4Aep5vAVlrIQ7P8+ +uWWgcHIFYa02TmhBUB/hjo0JANCQvAtpVNuQ8NyKPlqnnq1cttePbSYVeA0rrnOs +DcfySAiyGBEY9zMjFfHJtH1wtrPcJEU8XIEY3xUlrAJE2CEuV9dVYgfEEydnvgLc +8Ug0WXSiARjqbnMW3l8jh6bYCp/UpL/gSM4mxdKrgpfyPoweGhlOWXc3RTS7cqM9 +T25acURGOSI6/g8GF0sNE4VZmUvHggSTmsbLeXMJzxDWO+xVehRmbQx3IkG7u++b +QdRwGIJcDNn7zHlDMHtQ0Z1DBV94fZNBwCULhCBB5g20XTGw//S7Fj2FPwyhAgMB +AAGjgYYwgYMwDgYDVR0PAQH/BAQDAgKEMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr +BgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBTWfAmQ/BUIQm/9 +/llJJs2jUMWzGzAfBgNVHSMEGDAWgBTWfAmQ/BUIQm/9/llJJs2jUMWzGzANBgkq +hkiG9w0BAQsFAAOCAQEAvtcZFAELKiTuOiAeYts6zeKxc+nnHCzayDeD/BDCbxGJ +e1n+xdHjLtWGd+/Anc+fvftSYBPTFQqCi84lPiUIln5z/rUxE+ke81hNPIfw2obc +yIg87xCabQpVyEh8s+MV+7YPQ1+fH4FuSi2Fck1FejxkVqN2uOZPvOYUmSTsaVr1 +8SfRnwJNZ9UMRPM2bD4Jkvj0VcL42JM3QkOClOzYW4j/vll2cSs4kx7er27cIoo1 +Ck0v2xSPAiVjg6w65rUQeW6uB5m0T2wyj+wm0At8vzhZPlgS1fKhcmT2dzOq3+oN +R+IdLiXcyIkg0m9N8I17p0ljCSkbrgGMD3bbePRTfg== +-----END CERTIFICATE-----"; + + // Define the locator + let mut endpoint: EndPoint = format!("quic/localhost:{}", 18040).parse().unwrap(); + endpoint + .config_mut() + .extend( + [ + (TLS_ROOT_CA_CERTIFICATE_RAW, ca), + (TLS_SERVER_PRIVATE_KEY_RAW, key), + (TLS_SERVER_CERTIFICATE_RAW, cert), + ] + .iter() + .map(|(k, v)| ((*k).to_owned(), (*v).to_owned())), + ) + .unwrap(); + + task::block_on(multilink_transport(&endpoint)); + } +} diff --git a/io/zenoh-transport/tests/unicast_openclose.rs b/io/zenoh-transport/tests/unicast_openclose.rs index 464edbe236..b992a747a3 100644 --- a/io/zenoh-transport/tests/unicast_openclose.rs +++ b/io/zenoh-transport/tests/unicast_openclose.rs @@ -13,13 +13,14 @@ // use async_std::{prelude::FutureExt, task}; use std::{convert::TryFrom, sync::Arc, time::Duration}; -use zenoh_core::zasync_executor_init; +use zenoh_core::{zasync_executor_init, zcondfeat}; use zenoh_link::EndPoint; use zenoh_protocol::core::{WhatAmI, ZenohId}; use zenoh_result::ZResult; use zenoh_transport::{ - DummyTransportPeerEventHandler, TransportEventHandler, TransportManager, TransportMulticast, - TransportMulticastEventHandler, TransportPeer, TransportPeerEventHandler, TransportUnicast, + test_helpers::make_transport_manager_builder, DummyTransportPeerEventHandler, + TransportEventHandler, TransportManager, TransportMulticast, TransportMulticastEventHandler, + TransportPeer, TransportPeerEventHandler, TransportUnicast, }; const TIMEOUT: Duration = Duration::from_secs(60); @@ -78,15 +79,22 @@ impl TransportEventHandler for SHClientOpenClose { } } -async fn openclose_transport(endpoint: &EndPoint) { +async fn openclose_transport( + endpoint: &EndPoint, + #[cfg(feature = "shared-memory")] shm_transport: bool, +) { /* [ROUTER] */ let router_id = ZenohId::try_from([1]).unwrap(); let router_handler = Arc::new(SHRouterOpenClose); // Create the router transport manager - let unicast = TransportManager::config_unicast() - .max_links(2) - .max_sessions(1); + let unicast = make_transport_manager_builder( + #[cfg(feature = "transport_multilink")] + 2, + #[cfg(feature = "shared-memory")] + shm_transport, + ) + .max_sessions(1); let router_manager = TransportManager::builder() .whatami(WhatAmI::Router) .zid(router_id) @@ -99,9 +107,13 @@ async fn openclose_transport(endpoint: &EndPoint) { let client02_id = ZenohId::try_from([3]).unwrap(); // Create the transport transport manager for the first client - let unicast = TransportManager::config_unicast() - .max_links(2) - .max_sessions(1); + let unicast = make_transport_manager_builder( + #[cfg(feature = "transport_multilink")] + 2, + #[cfg(feature = "shared-memory")] + shm_transport, + ) + .max_sessions(1); let client01_manager = TransportManager::builder() .whatami(WhatAmI::Client) .zid(client01_id) @@ -110,9 +122,13 @@ async fn openclose_transport(endpoint: &EndPoint) { .unwrap(); // Create the transport transport manager for the second client - let unicast = TransportManager::config_unicast() - .max_links(1) - .max_sessions(1); + let unicast = make_transport_manager_builder( + #[cfg(feature = "transport_multilink")] + 1, + #[cfg(feature = "shared-memory")] + shm_transport, + ) + .max_sessions(1); let client02_manager = TransportManager::builder() .whatami(WhatAmI::Client) .zid(client02_id) @@ -136,12 +152,12 @@ async fn openclose_transport(endpoint: &EndPoint) { let mut links_num = 1; println!("Transport Open Close [1c1]"); - let res = ztimeout!(client01_manager.open_transport(endpoint.clone())); + let res = ztimeout!(client01_manager.open_transport_unicast(endpoint.clone())); println!("Transport Open Close [1c2]: {res:?}"); assert!(res.is_ok()); let c_ses1 = res.unwrap(); println!("Transport Open Close [1d1]"); - let transports = client01_manager.get_transports(); + let transports = ztimeout!(client01_manager.get_transports_unicast()); println!("Transport Open Close [1d2]: {transports:?}"); assert_eq!(transports.len(), 1); assert_eq!(c_ses1.get_zid().unwrap(), router_id); @@ -154,7 +170,7 @@ async fn openclose_transport(endpoint: &EndPoint) { println!("Transport Open Close [1f1]"); ztimeout!(async { loop { - let transports = router_manager.get_transports(); + let transports = ztimeout!(router_manager.get_transports_unicast()); let s = transports .iter() .find(|s| s.get_zid().unwrap() == client01_id); @@ -173,51 +189,56 @@ async fn openclose_transport(endpoint: &EndPoint) { /* [2] */ // Open a second transport from the client to the router // -> This should be accepted - links_num = 2; - - println!("\nTransport Open Close [2a1]"); - let res = ztimeout!(client01_manager.open_transport(endpoint.clone())); - println!("Transport Open Close [2a2]: {res:?}"); - assert!(res.is_ok()); - let c_ses2 = res.unwrap(); - println!("Transport Open Close [2b1]"); - let transports = client01_manager.get_transports(); - println!("Transport Open Close [2b2]: {transports:?}"); - assert_eq!(transports.len(), 1); - assert_eq!(c_ses2.get_zid().unwrap(), router_id); - println!("Transport Open Close [2c1]"); - let links = c_ses2.get_links().unwrap(); - println!("Transport Open Close [2c2]: {links:?}"); - assert_eq!(links.len(), links_num); - assert_eq!(c_ses2, c_ses1); - - // Verify that the transport has been open on the router - println!("Transport Open Close [2d1]"); - ztimeout!(async { - loop { - let transports = router_manager.get_transports(); - let s = transports - .iter() - .find(|s| s.get_zid().unwrap() == client01_id) - .unwrap(); - - let links = s.get_links().unwrap(); - if links.len() == links_num { - break; + // (this stage is ignored for SHM transport, because it supports only one link) + if zcondfeat!("shared-memory", !shm_transport, true) { + links_num = 2; + + println!("\nTransport Open Close [2a1]"); + let res = ztimeout!(client01_manager.open_transport_unicast(endpoint.clone())); + println!("Transport Open Close [2a2]: {res:?}"); + assert!(res.is_ok()); + let c_ses2 = res.unwrap(); + println!("Transport Open Close [2b1]"); + let transports = ztimeout!(client01_manager.get_transports_unicast()); + println!("Transport Open Close [2b2]: {transports:?}"); + assert_eq!(transports.len(), 1); + assert_eq!(c_ses2.get_zid().unwrap(), router_id); + println!("Transport Open Close [2c1]"); + let links = c_ses2.get_links().unwrap(); + println!("Transport Open Close [2c2]: {links:?}"); + assert_eq!(links.len(), links_num); + assert_eq!(c_ses2, c_ses1); + + // Verify that the transport has been open on the router + println!("Transport Open Close [2d1]"); + ztimeout!(async { + loop { + let transports = ztimeout!(router_manager.get_transports_unicast()); + let s = transports + .iter() + .find(|s| s.get_zid().unwrap() == client01_id) + .unwrap(); + + let links = s.get_links().unwrap(); + if links.len() == links_num { + break; + } + task::sleep(SLEEP).await; } - task::sleep(SLEEP).await; - } - }); + }); + } else { + println!("\nTransport Open Close [2a*]: step ignored for SHM transport!"); + } /* [3] */ // Open transport -> This should be rejected because // of the maximum limit of links per transport println!("\nTransport Open Close [3a1]"); - let res = ztimeout!(client01_manager.open_transport(endpoint.clone())); + let res = ztimeout!(client01_manager.open_transport_unicast(endpoint.clone())); println!("Transport Open Close [3a2]: {res:?}"); assert!(res.is_err()); println!("Transport Open Close [3b1]"); - let transports = client01_manager.get_transports(); + let transports = ztimeout!(client01_manager.get_transports_unicast()); println!("Transport Open Close [3b2]: {transports:?}"); assert_eq!(transports.len(), 1); assert_eq!(c_ses1.get_zid().unwrap(), router_id); @@ -230,7 +251,7 @@ async fn openclose_transport(endpoint: &EndPoint) { println!("Transport Open Close [3d1]"); ztimeout!(async { task::sleep(SLEEP).await; - let transports = router_manager.get_transports(); + let transports = ztimeout!(router_manager.get_transports_unicast()); assert_eq!(transports.len(), 1); let s = transports .iter() @@ -247,7 +268,7 @@ async fn openclose_transport(endpoint: &EndPoint) { println!("Transport Open Close [4a2]: {res:?}"); assert!(res.is_ok()); println!("Transport Open Close [4b1]"); - let transports = client01_manager.get_transports(); + let transports = ztimeout!(client01_manager.get_transports_unicast()); println!("Transport Open Close [4b2]: {transports:?}"); assert_eq!(transports.len(), 0); @@ -255,7 +276,7 @@ async fn openclose_transport(endpoint: &EndPoint) { println!("Transport Open Close [4c1]"); ztimeout!(async { loop { - let transports = router_manager.get_transports(); + let transports = ztimeout!(router_manager.get_transports_unicast()); let index = transports .iter() .find(|s| s.get_zid().unwrap() == client01_id); @@ -272,12 +293,12 @@ async fn openclose_transport(endpoint: &EndPoint) { links_num = 1; println!("\nTransport Open Close [5a1]"); - let res = ztimeout!(client01_manager.open_transport(endpoint.clone())); + let res = ztimeout!(client01_manager.open_transport_unicast(endpoint.clone())); println!("Transport Open Close [5a2]: {res:?}"); assert!(res.is_ok()); let c_ses3 = res.unwrap(); println!("Transport Open Close [5b1]"); - let transports = client01_manager.get_transports(); + let transports = ztimeout!(client01_manager.get_transports_unicast()); println!("Transport Open Close [5b2]: {transports:?}"); assert_eq!(transports.len(), 1); assert_eq!(c_ses3.get_zid().unwrap(), router_id); @@ -290,7 +311,7 @@ async fn openclose_transport(endpoint: &EndPoint) { println!("Transport Open Close [5d1]"); ztimeout!(async { task::sleep(SLEEP).await; - let transports = router_manager.get_transports(); + let transports = ztimeout!(router_manager.get_transports_unicast()); assert_eq!(transports.len(), 1); let s = transports .iter() @@ -304,11 +325,11 @@ async fn openclose_transport(endpoint: &EndPoint) { // Open transport -> This should be rejected because // of the maximum limit of transports println!("\nTransport Open Close [6a1]"); - let res = ztimeout!(client02_manager.open_transport(endpoint.clone())); + let res = ztimeout!(client02_manager.open_transport_unicast(endpoint.clone())); println!("Transport Open Close [6a2]: {res:?}"); assert!(res.is_err()); println!("Transport Open Close [6b1]"); - let transports = client02_manager.get_transports(); + let transports = ztimeout!(client02_manager.get_transports_unicast()); println!("Transport Open Close [6b2]: {transports:?}"); assert_eq!(transports.len(), 0); @@ -316,7 +337,7 @@ async fn openclose_transport(endpoint: &EndPoint) { println!("Transport Open Close [6c1]"); ztimeout!(async { task::sleep(SLEEP).await; - let transports = router_manager.get_transports(); + let transports = ztimeout!(router_manager.get_transports_unicast()); assert_eq!(transports.len(), 1); let s = transports .iter() @@ -333,7 +354,7 @@ async fn openclose_transport(endpoint: &EndPoint) { println!("Transport Open Close [7a2]: {res:?}"); assert!(res.is_ok()); println!("Transport Open Close [7b1]"); - let transports = client01_manager.get_transports(); + let transports = ztimeout!(client01_manager.get_transports_unicast()); println!("Transport Open Close [7b2]: {transports:?}"); assert_eq!(transports.len(), 0); @@ -341,7 +362,7 @@ async fn openclose_transport(endpoint: &EndPoint) { println!("Transport Open Close [7c1]"); ztimeout!(async { loop { - let transports = router_manager.get_transports(); + let transports = ztimeout!(router_manager.get_transports_unicast()); if transports.is_empty() { break; } @@ -355,12 +376,12 @@ async fn openclose_transport(endpoint: &EndPoint) { links_num = 1; println!("\nTransport Open Close [8a1]"); - let res = ztimeout!(client02_manager.open_transport(endpoint.clone())); + let res = ztimeout!(client02_manager.open_transport_unicast(endpoint.clone())); println!("Transport Open Close [8a2]: {res:?}"); assert!(res.is_ok()); let c_ses4 = res.unwrap(); println!("Transport Open Close [8b1]"); - let transports = client02_manager.get_transports(); + let transports = ztimeout!(client02_manager.get_transports_unicast()); println!("Transport Open Close [8b2]: {transports:?}"); assert_eq!(transports.len(), 1); println!("Transport Open Close [8c1]"); @@ -372,7 +393,7 @@ async fn openclose_transport(endpoint: &EndPoint) { println!("Transport Open Close [8d1]"); ztimeout!(async { loop { - let transports = router_manager.get_transports(); + let transports = ztimeout!(router_manager.get_transports_unicast()); let s = transports .iter() .find(|s| s.get_zid().unwrap() == client02_id); @@ -394,7 +415,7 @@ async fn openclose_transport(endpoint: &EndPoint) { println!("Transport Open Close [9a2]: {res:?}"); assert!(res.is_ok()); println!("Transport Open Close [9b1]"); - let transports = client02_manager.get_transports(); + let transports = ztimeout!(client02_manager.get_transports_unicast()); println!("Transport Open Close [9b2]: {transports:?}"); assert_eq!(transports.len(), 0); @@ -402,7 +423,7 @@ async fn openclose_transport(endpoint: &EndPoint) { println!("Transport Open Close [9c1]"); ztimeout!(async { loop { - let transports = router_manager.get_transports(); + let transports = ztimeout!(router_manager.get_transports_unicast()); if transports.is_empty() { break; } @@ -434,6 +455,20 @@ async fn openclose_transport(endpoint: &EndPoint) { task::sleep(SLEEP).await; } +async fn openclose_net_transport(endpoint: &EndPoint) { + openclose_transport( + endpoint, + #[cfg(feature = "shared-memory")] + false, + ) + .await +} + +#[cfg(feature = "shared-memory")] +async fn openclose_shm_transport(endpoint: &EndPoint) { + openclose_transport(endpoint, true).await +} + #[cfg(feature = "transport_tcp")] #[test] fn openclose_tcp_only() { @@ -443,7 +478,19 @@ fn openclose_tcp_only() { }); let endpoint: EndPoint = format!("tcp/127.0.0.1:{}", 13000).parse().unwrap(); - task::block_on(openclose_transport(&endpoint)); + task::block_on(openclose_net_transport(&endpoint)); +} + +#[cfg(all(feature = "transport_tcp", feature = "shared-memory"))] +#[test] +fn openclose_tcp_only_with_shm_transport() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + let endpoint: EndPoint = format!("tcp/127.0.0.1:{}", 13100).parse().unwrap(); + task::block_on(openclose_shm_transport(&endpoint)); } #[cfg(feature = "transport_udp")] @@ -455,11 +502,24 @@ fn openclose_udp_only() { }); let endpoint: EndPoint = format!("udp/127.0.0.1:{}", 13010).parse().unwrap(); - task::block_on(openclose_transport(&endpoint)); + task::block_on(openclose_net_transport(&endpoint)); +} + +#[cfg(all(feature = "transport_udp", feature = "shared-memory"))] +#[test] +fn openclose_udp_only_with_shm_transport() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + let endpoint: EndPoint = format!("udp/127.0.0.1:{}", 13110).parse().unwrap(); + task::block_on(openclose_shm_transport(&endpoint)); } #[cfg(feature = "transport_ws")] #[test] +#[ignore] fn openclose_ws_only() { let _ = env_logger::try_init(); task::block_on(async { @@ -467,11 +527,51 @@ fn openclose_ws_only() { }); let endpoint: EndPoint = format!("ws/127.0.0.1:{}", 13020).parse().unwrap(); - task::block_on(openclose_transport(&endpoint)); + task::block_on(openclose_net_transport(&endpoint)); +} + +#[cfg(all(feature = "transport_ws", feature = "shared-memory"))] +#[test] +#[ignore] +fn openclose_ws_only_with_shm_transport() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + let endpoint: EndPoint = format!("ws/127.0.0.1:{}", 13120).parse().unwrap(); + task::block_on(openclose_shm_transport(&endpoint)); +} + +#[cfg(feature = "transport_shm")] +#[test] +#[ignore] +fn openclose_shm_only() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + let endpoint: EndPoint = "shm/openclose_shm_only".parse().unwrap(); + task::block_on(openclose_net_transport(&endpoint)); +} + +#[cfg(all(feature = "transport_shm", feature = "shared-memory"))] +#[test] +#[ignore] +fn openclose_shm_only_with_shm_transport() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + let endpoint: EndPoint = "shm/openclose_shm_only_with_shm_transport".parse().unwrap(); + task::block_on(openclose_shm_transport(&endpoint)); } #[cfg(all(feature = "transport_unixsock-stream", target_family = "unix"))] #[test] +#[ignore] fn openclose_unix_only() { let _ = env_logger::try_init(); task::block_on(async { @@ -481,7 +581,7 @@ fn openclose_unix_only() { let f1 = "zenoh-test-unix-socket-9.sock"; let _ = std::fs::remove_file(f1); let endpoint: EndPoint = format!("unixsock-stream/{f1}").parse().unwrap(); - task::block_on(openclose_transport(&endpoint)); + task::block_on(openclose_net_transport(&endpoint)); let _ = std::fs::remove_file(f1); let _ = std::fs::remove_file(format!("{f1}.lock")); } @@ -585,7 +685,7 @@ R+IdLiXcyIkg0m9N8I17p0ljCSkbrgGMD3bbePRTfg== ) .unwrap(); - task::block_on(openclose_transport(&endpoint)); + task::block_on(openclose_net_transport(&endpoint)); } #[cfg(feature = "transport_quic")] @@ -687,5 +787,5 @@ R+IdLiXcyIkg0m9N8I17p0ljCSkbrgGMD3bbePRTfg== ) .unwrap(); - task::block_on(openclose_transport(&endpoint)); + task::block_on(openclose_net_transport(&endpoint)); } diff --git a/io/zenoh-transport/tests/unicast_conduits.rs b/io/zenoh-transport/tests/unicast_priorities.rs similarity index 60% rename from io/zenoh-transport/tests/unicast_conduits.rs rename to io/zenoh-transport/tests/unicast_priorities.rs index 9ae450faf8..589cfe8e04 100644 --- a/io/zenoh-transport/tests/unicast_conduits.rs +++ b/io/zenoh-transport/tests/unicast_priorities.rs @@ -19,12 +19,19 @@ use std::fmt::Write as _; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::time::Duration; -use zenoh_buffers::ZBuf; use zenoh_core::zasync_executor_init; use zenoh_link::Link; +use zenoh_protocol::network::NetworkBody; use zenoh_protocol::{ - core::{Channel, CongestionControl, EndPoint, Priority, Reliability, WhatAmI, ZenohId}, - zenoh::ZenohMessage, + core::{CongestionControl, Encoding, EndPoint, Priority, WhatAmI, ZenohId}, + network::{ + push::{ + ext::{NodeIdType, QoSType}, + Push, + }, + NetworkMessage, + }, + zenoh::Put, }; use zenoh_result::ZResult; use zenoh_transport::{ @@ -58,20 +65,28 @@ macro_rules! ztimeout { // Transport Handler for the router struct SHRouter { - priority: Priority, + priority: Arc, count: Arc, } impl SHRouter { - fn new(priority: Priority) -> Self { + fn new() -> Self { Self { - priority, + priority: Arc::new(AtomicUsize::new(0)), count: Arc::new(AtomicUsize::new(0)), } } + fn set_priority(&self, priority: Priority) { + self.priority.store(priority as usize, Ordering::Relaxed) + } + + fn reset_count(&self) { + self.count.store(0, Ordering::Relaxed) + } + fn get_count(&self) -> usize { - self.count.load(Ordering::SeqCst) + self.count.load(Ordering::Relaxed) } } @@ -81,34 +96,42 @@ impl TransportEventHandler for SHRouter { _peer: TransportPeer, _transport: TransportUnicast, ) -> ZResult> { - let arc = Arc::new(SCRouter::new(self.count.clone(), self.priority)); + let arc = Arc::new(SCRouter::new(self.priority.clone(), self.count.clone())); Ok(arc) } fn new_multicast( &self, - _transport: TransportMulticast, - ) -> ZResult> { + _transport: zenoh_transport::TransportMulticast, + ) -> ZResult> { panic!(); } } // Transport Callback for the router pub struct SCRouter { - priority: Priority, + priority: Arc, count: Arc, } impl SCRouter { - pub fn new(count: Arc, priority: Priority) -> Self { + pub fn new(priority: Arc, count: Arc) -> Self { Self { priority, count } } } impl TransportPeerEventHandler for SCRouter { - fn handle_message(&self, message: ZenohMessage) -> ZResult<()> { - assert_eq!(self.priority, message.channel.priority); - self.count.fetch_add(1, Ordering::SeqCst); + fn handle_message(&self, message: NetworkMessage) -> ZResult<()> { + match &message.body { + NetworkBody::Push(p) => { + assert_eq!( + self.priority.load(Ordering::Relaxed), + p.ext_qos.get_priority() as usize + ); + } + _ => panic!("Unexpected message"), + } + self.count.fetch_add(1, Ordering::Relaxed); Ok(()) } @@ -158,7 +181,7 @@ impl Default for SCClient { } impl TransportPeerEventHandler for SCClient { - fn handle_message(&self, _message: ZenohMessage) -> ZResult<()> { + fn handle_message(&self, _message: NetworkMessage) -> ZResult<()> { Ok(()) } @@ -172,9 +195,8 @@ impl TransportPeerEventHandler for SCClient { } } -async fn open_transport( +async fn open_transport_unicast( endpoints: &[EndPoint], - priority: Priority, ) -> ( TransportManager, Arc, @@ -186,7 +208,7 @@ async fn open_transport( let router_id = ZenohId::try_from([2]).unwrap(); // Create the router transport manager - let router_handler = Arc::new(SHRouter::new(priority)); + let router_handler = Arc::new(SHRouter::new()); let router_manager = TransportManager::builder() .whatami(WhatAmI::Router) .zid(router_id) @@ -210,10 +232,13 @@ async fn open_transport( // Open transport -> This should be accepted for e in endpoints.iter() { println!("Opening transport with {e}"); - let _ = ztimeout!(client_manager.open_transport(e.clone())).unwrap(); + let _ = ztimeout!(client_manager.open_transport_unicast(e.clone())).unwrap(); } - let client_transport = client_manager.get_transport(&router_id).unwrap(); + let client_transport = client_manager + .get_transport_unicast(&router_id) + .await + .unwrap(); // Return the handlers ( @@ -239,7 +264,7 @@ async fn close_transport( ztimeout!(client_transport.close()).unwrap(); ztimeout!(async { - while !router_manager.get_transports().is_empty() { + while !router_manager.get_transports_unicast().await.is_empty() { task::sleep(SLEEP).await; } }); @@ -260,93 +285,93 @@ async fn close_transport( task::sleep(SLEEP).await; } -async fn single_run( - router_handler: Arc, - client_transport: TransportUnicast, - channel: Channel, - msg_size: usize, -) { - // Create the message to send - let key = "test".into(); - let payload = ZBuf::from(vec![0_u8; msg_size]); - let data_info = None; - let routing_context = None; - let reply_context = None; - let attachment = None; - let message = ZenohMessage::make_data( - key, - payload, - channel, - CongestionControl::Block, - data_info, - routing_context, - reply_context, - attachment, - ); - - println!("Sending {MSG_COUNT} messages... {channel:?} {msg_size}"); - for _ in 0..MSG_COUNT { - client_transport.schedule(message.clone()).unwrap(); - } - - // Wait for the messages to arrive to the other side - ztimeout!(async { - while router_handler.get_count() != MSG_COUNT { - task::sleep(SLEEP_COUNT).await; +async fn single_run(router_handler: Arc, client_transport: TransportUnicast) { + for p in PRIORITY_ALL.iter() { + for ms in MSG_SIZE_ALL.iter() { + // Reset the counter and set priority on the router + router_handler.reset_count(); + router_handler.set_priority(*p); + + // Create the message to send + let message: NetworkMessage = Push { + wire_expr: "test".into(), + ext_qos: QoSType::new(*p, CongestionControl::Block, false), + ext_tstamp: None, + ext_nodeid: NodeIdType::default(), + payload: Put { + payload: vec![0u8; *ms].into(), + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + } + .into(), + } + .into(); + + println!("Sending {MSG_COUNT} messages... {p:?} {ms}"); + for _ in 0..MSG_COUNT { + client_transport.schedule(message.clone()).unwrap(); + } + + // Wait for the messages to arrive to the other side + ztimeout!(async { + while router_handler.get_count() != MSG_COUNT { + task::sleep(SLEEP_COUNT).await; + } + }); } - }); + } // Wait a little bit task::sleep(SLEEP).await; } -async fn run(endpoints: &[EndPoint], channel: &[Channel], msg_size: &[usize]) { - for ch in channel.iter() { - for ms in msg_size.iter() { - let (router_manager, router_handler, client_manager, client_transport) = - open_transport(endpoints, ch.priority).await; - single_run(router_handler.clone(), client_transport.clone(), *ch, *ms).await; - close_transport(router_manager, client_manager, client_transport, endpoints).await; - } - } +async fn run(endpoints: &[EndPoint]) { + let (router_manager, router_handler, client_manager, client_transport) = + open_transport_unicast(endpoints).await; + single_run(router_handler.clone(), client_transport.clone()).await; + close_transport(router_manager, client_manager, client_transport, endpoints).await; } #[cfg(feature = "transport_tcp")] #[test] -fn conduits_tcp_only() { +fn priorities_tcp_only() { let _ = env_logger::try_init(); task::block_on(async { zasync_executor_init!(); }); - let mut channel = vec![]; - for p in PRIORITY_ALL.iter() { - channel.push(Channel { - priority: *p, - reliability: Reliability::Reliable, - }); - } // Define the locators let endpoints: Vec = vec![format!("tcp/127.0.0.1:{}", 10000).parse().unwrap()]; // Run - task::block_on(run(&endpoints, &channel, &MSG_SIZE_ALL)); + task::block_on(run(&endpoints)); +} + +#[cfg(feature = "transport_shm")] +#[test] +#[ignore] +fn conduits_shm_only() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + // Define the locators + let endpoints: Vec = vec!["shm/conduits_shm_only".to_string().parse().unwrap()]; + // Run + task::block_on(run(&endpoints)); } #[cfg(feature = "transport_ws")] #[test] -fn conduits_ws_only() { +fn priorities_ws_only() { let _ = env_logger::try_init(); task::block_on(async { zasync_executor_init!(); }); - let mut channel = vec![]; - for p in PRIORITY_ALL.iter() { - channel.push(Channel { - priority: *p, - reliability: Reliability::Reliable, - }); - } // Define the locators let endpoints: Vec = vec![format!("ws/127.0.0.1:{}", 10010).parse().unwrap()]; // Run - task::block_on(run(&endpoints, &channel, &MSG_SIZE_ALL)); + task::block_on(run(&endpoints)); } diff --git a/io/zenoh-transport/tests/unicast_shm.rs b/io/zenoh-transport/tests/unicast_shm.rs index e109368ec9..f0448d5a4d 100644 --- a/io/zenoh-transport/tests/unicast_shm.rs +++ b/io/zenoh-transport/tests/unicast_shm.rs @@ -16,28 +16,29 @@ mod tests { use async_std::{prelude::FutureExt, task}; use std::{ any::Any, - collections::HashSet, convert::TryFrom, - iter::FromIterator, sync::{ atomic::{AtomicUsize, Ordering}, Arc, }, time::Duration, }; - use zenoh_buffers::{SplitBuffer, ZBuf}; + use zenoh_buffers::SplitBuffer; use zenoh_core::zasync_executor_init; use zenoh_link::Link; use zenoh_protocol::{ - core::{Channel, CongestionControl, EndPoint, Priority, Reliability, WhatAmI, ZenohId}, - zenoh::{Data, ZenohBody, ZenohMessage}, + core::{CongestionControl, Encoding, EndPoint, Priority, WhatAmI, ZenohId}, + network::{ + push::ext::{NodeIdType, QoSType}, + NetworkBody, NetworkMessage, Push, + }, + zenoh::{PushBody, Put}, }; use zenoh_result::ZResult; - use zenoh_shm::SharedMemoryManager; + use zenoh_shm::{SharedMemoryBuf, SharedMemoryManager}; use zenoh_transport::{ - unicast::establishment::authenticator::SharedMemoryAuthenticator, TransportEventHandler, - TransportManager, TransportMulticast, TransportMulticastEventHandler, TransportPeer, - TransportPeerEventHandler, TransportUnicast, + TransportEventHandler, TransportManager, TransportMulticast, + TransportMulticastEventHandler, TransportPeer, TransportPeerEventHandler, TransportUnicast, }; const TIMEOUT: Duration = Duration::from_secs(60); @@ -103,14 +104,27 @@ mod tests { } impl TransportPeerEventHandler for SCPeer { - fn handle_message(&self, message: ZenohMessage) -> ZResult<()> { + fn handle_message(&self, message: NetworkMessage) -> ZResult<()> { if self.is_shm { print!("s"); } else { print!("n"); } let payload = match message.body { - ZenohBody::Data(Data { payload, .. }) => payload.contiguous().into_owned(), + NetworkBody::Push(m) => match m.payload { + PushBody::Put(Put { payload, .. }) => { + for zs in payload.zslices() { + if self.is_shm && zs.downcast_ref::().is_none() { + panic!("Expected SharedMemoryBuf: {:?}", zs); + } else if !self.is_shm && zs.downcast_ref::().is_some() + { + panic!("Not Expected SharedMemoryBuf: {:?}", zs); + } + } + payload.contiguous().into_owned() + } + _ => panic!("Unsolicited message"), + }, _ => panic!("Unsolicited message"), }; assert_eq!(payload.len(), MSG_SIZE); @@ -149,28 +163,20 @@ mod tests { .unwrap(); // Create a peer manager with shared-memory authenticator enabled - let peer_shm01_handler = Arc::new(SHPeer::new(false)); - let unicast = - TransportManager::config_unicast().peer_authenticator(HashSet::from_iter(vec![ - SharedMemoryAuthenticator::make().unwrap().into(), - ])); + let peer_shm01_handler = Arc::new(SHPeer::new(true)); let peer_shm01_manager = TransportManager::builder() .whatami(WhatAmI::Peer) .zid(peer_shm01) - .unicast(unicast) + .unicast(TransportManager::config_unicast().shm(true)) .build(peer_shm01_handler.clone()) .unwrap(); // Create a peer manager with shared-memory authenticator enabled let peer_shm02_handler = Arc::new(SHPeer::new(true)); - let unicast = - TransportManager::config_unicast().peer_authenticator(HashSet::from_iter(vec![ - SharedMemoryAuthenticator::make().unwrap().into(), - ])); let peer_shm02_manager = TransportManager::builder() .whatami(WhatAmI::Peer) .zid(peer_shm02) - .unicast(unicast) + .unicast(TransportManager::config_unicast().shm(true)) .build(peer_shm02_handler.clone()) .unwrap(); @@ -179,6 +185,7 @@ mod tests { let peer_net01_manager = TransportManager::builder() .whatami(WhatAmI::Peer) .zid(peer_net01) + .unicast(TransportManager::config_unicast().shm(false)) .build(peer_net01_handler.clone()) .unwrap(); @@ -191,18 +198,30 @@ mod tests { // Create a transport with the peer println!("Transport SHM [1b]"); - let _ = ztimeout!(peer_shm02_manager.open_transport(endpoint.clone())).unwrap(); + let peer_shm01_transport = + ztimeout!(peer_shm02_manager.open_transport_unicast(endpoint.clone())).unwrap(); + assert!(peer_shm01_transport.is_shm().unwrap()); // Create a transport with the peer println!("Transport SHM [1c]"); - let _ = ztimeout!(peer_net01_manager.open_transport(endpoint.clone())).unwrap(); + let peer_net02_transport = + ztimeout!(peer_net01_manager.open_transport_unicast(endpoint.clone())).unwrap(); + assert!(!peer_net02_transport.is_shm().unwrap()); // Retrieve the transports println!("Transport SHM [2a]"); - let peer_shm02_transport = peer_shm01_manager.get_transport(&peer_shm02).unwrap(); + let peer_shm02_transport = peer_shm01_manager + .get_transport_unicast(&peer_shm02) + .await + .unwrap(); + assert!(peer_shm02_transport.is_shm().unwrap()); println!("Transport SHM [2b]"); - let peer_net01_transport = peer_shm01_manager.get_transport(&peer_net01).unwrap(); + let peer_net01_transport = peer_shm01_manager + .get_transport_unicast(&peer_net01) + .await + .unwrap(); + assert!(!peer_net01_transport.is_shm().unwrap()); // Send the message println!("Transport SHM [3a]"); @@ -221,30 +240,24 @@ mod tests { let bs = unsafe { sbuf.as_mut_slice() }; bs[0..8].copy_from_slice(&msg_count.to_le_bytes()); - let key = "test".into(); - let payload: ZBuf = sbuf.into(); - let channel = Channel { - priority: Priority::default(), - reliability: Reliability::Reliable, - }; - let congestion_control = CongestionControl::Block; - let data_info = None; - let routing_context = None; - let reply_context = None; - let attachment = None; - - let message = ZenohMessage::make_data( - key, - payload, - channel, - congestion_control, - data_info, - routing_context, - reply_context, - attachment, - ); - - peer_shm02_transport.schedule(message.clone()).unwrap(); + let message: NetworkMessage = Push { + wire_expr: "test".into(), + ext_qos: QoSType::new(Priority::default(), CongestionControl::Block, false), + ext_tstamp: None, + ext_nodeid: NodeIdType::default(), + payload: Put { + payload: sbuf.into(), + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + ext_shm: None, + ext_unknown: vec![], + } + .into(), + } + .into(); + + peer_shm02_transport.schedule(message).unwrap(); } // Wait a little bit @@ -274,30 +287,24 @@ mod tests { let bs = unsafe { sbuf.as_mut_slice() }; bs[0..8].copy_from_slice(&msg_count.to_le_bytes()); - let key = "test".into(); - let payload: ZBuf = sbuf.into(); - let channel = Channel { - priority: Priority::default(), - reliability: Reliability::Reliable, - }; - let congestion_control = CongestionControl::Block; - let data_info = None; - let routing_context = None; - let reply_context = None; - let attachment = None; - - let message = ZenohMessage::make_data( - key, - payload, - channel, - congestion_control, - data_info, - routing_context, - reply_context, - attachment, - ); - - peer_net01_transport.schedule(message.clone()).unwrap(); + let message: NetworkMessage = Push { + wire_expr: "test".into(), + ext_qos: QoSType::new(Priority::default(), CongestionControl::Block, false), + ext_tstamp: None, + ext_nodeid: NodeIdType::default(), + payload: Put { + payload: sbuf.into(), + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + ext_shm: None, + ext_unknown: vec![], + } + .into(), + } + .into(); + + peer_net01_transport.schedule(message).unwrap(); } // Wait a little bit @@ -322,7 +329,7 @@ mod tests { ztimeout!(peer_net01_transport.close()).unwrap(); ztimeout!(async { - while !peer_shm01_manager.get_transports().is_empty() { + while !peer_shm01_manager.get_transports_unicast().await.is_empty() { task::sleep(SLEEP).await; } }); @@ -370,4 +377,16 @@ mod tests { let endpoint: EndPoint = format!("ws/127.0.0.1:{}", 14010).parse().unwrap(); task::block_on(run(&endpoint)); } + + #[cfg(all(feature = "transport_shm", feature = "shared-memory"))] + #[test] + fn transport_shm_shm() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + let endpoint: EndPoint = "shm/transport_shm_shm".parse().unwrap(); + task::block_on(run(&endpoint)); + } } diff --git a/io/zenoh-transport/tests/unicast_simultaneous.rs b/io/zenoh-transport/tests/unicast_simultaneous.rs index 659c52a260..f3304606e3 100644 --- a/io/zenoh-transport/tests/unicast_simultaneous.rs +++ b/io/zenoh-transport/tests/unicast_simultaneous.rs @@ -20,12 +20,15 @@ mod tests { use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::time::Duration; - use zenoh_buffers::ZBuf; use zenoh_core::zasync_executor_init; use zenoh_link::Link; use zenoh_protocol::{ - core::{Channel, CongestionControl, EndPoint, Priority, Reliability, WhatAmI, ZenohId}, - zenoh::ZenohMessage, + core::{CongestionControl, Encoding, EndPoint, Priority, WhatAmI, ZenohId}, + network::{ + push::ext::{NodeIdType, QoSType}, + NetworkMessage, Push, + }, + zenoh::Put, }; use zenoh_result::ZResult; use zenoh_transport::{ @@ -71,32 +74,27 @@ mod tests { transport: TransportUnicast, ) -> ZResult> { // Create the message to send - let key = "test".into(); - let payload = ZBuf::from(vec![0_u8; MSG_SIZE]); - let channel = Channel { - priority: Priority::Control, - reliability: Reliability::Reliable, - }; - let congestion_control = CongestionControl::Block; - let data_info = None; - let routing_context = None; - let reply_context = None; - let attachment = None; - - let message = ZenohMessage::make_data( - key, - payload, - channel, - congestion_control, - data_info, - routing_context, - reply_context, - attachment, - ); + let message: NetworkMessage = Push { + wire_expr: "test".into(), + ext_qos: QoSType::new(Priority::Control, CongestionControl::Block, false), + ext_tstamp: None, + ext_nodeid: NodeIdType::default(), + payload: Put { + payload: vec![0u8; MSG_SIZE].into(), + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + } + .into(), + } + .into(); println!("[Simultaneous {}] Sending {}...", self.zid, MSG_COUNT); for _ in 0..MSG_COUNT { - transport.handle_message(message.clone()).unwrap(); + transport.schedule(message.clone()).unwrap(); } println!("[Simultaneous {}] ... sent {}", self.zid, MSG_COUNT); @@ -123,7 +121,7 @@ mod tests { } impl TransportPeerEventHandler for MHPeer { - fn handle_message(&self, _msg: ZenohMessage) -> ZResult<()> { + fn handle_message(&self, _msg: NetworkMessage) -> ZResult<()> { self.count.fetch_add(1, Ordering::AcqRel); Ok(()) } @@ -194,13 +192,13 @@ mod tests { // These open should succeed for e in c_ep02.iter() { println!("[Simultaneous 01c] => Opening transport with {e:?}..."); - let _ = ztimeout!(c_p01m.open_transport(e.clone())).unwrap(); + let _ = ztimeout!(c_p01m.open_transport_unicast(e.clone())).unwrap(); } // These open should fails for e in c_ep02.iter() { println!("[Simultaneous 01d] => Exceeding transport with {e:?}..."); - let res = ztimeout!(c_p01m.open_transport(e.clone())); + let res = ztimeout!(c_p01m.open_transport_unicast(e.clone())); assert!(res.is_err()); } @@ -212,9 +210,9 @@ mod tests { task::sleep(SLEEP).await; println!( "[Simultaneous 01e] => Transports: {:?}", - peer01_manager.get_transports() + peer01_manager.get_transports_unicast().await ); - tp02 = peer01_manager.get_transport(&peer_id02); + tp02 = peer01_manager.get_transport_unicast(&peer_id02).await; } tp02.unwrap() @@ -249,13 +247,13 @@ mod tests { // These open should succeed for e in c_ep01.iter() { println!("[Simultaneous 02c] => Opening transport with {e:?}..."); - let _ = ztimeout!(c_p02m.open_transport(e.clone())).unwrap(); + let _ = ztimeout!(c_p02m.open_transport_unicast(e.clone())).unwrap(); } // These open should fails for e in c_ep01.iter() { println!("[Simultaneous 02d] => Exceeding transport with {e:?}..."); - let res = ztimeout!(c_p02m.open_transport(e.clone())); + let res = ztimeout!(c_p02m.open_transport_unicast(e.clone())); assert!(res.is_err()); } @@ -268,9 +266,9 @@ mod tests { task::sleep(SLEEP).await; println!( "[Simultaneous 02e] => Transports: {:?}", - peer02_manager.get_transports() + peer02_manager.get_transports_unicast().await ); - tp01 = peer02_manager.get_transport(&peer_id01); + tp01 = peer02_manager.get_transport_unicast(&peer_id01).await; } tp01.unwrap() }); @@ -331,8 +329,36 @@ mod tests { }); } + #[cfg(feature = "transport_shm")] + #[test] + #[ignore] + fn transport_shm_simultaneous() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + let endpoint01: Vec = vec![ + "shm/transport_shm_simultaneous".parse().unwrap(), + "shm/transport_shm_simultaneous2".parse().unwrap(), + "shm/transport_shm_simultaneous3".parse().unwrap(), + "shm/transport_shm_simultaneous4".parse().unwrap(), + ]; + let endpoint02: Vec = vec![ + "shm/transport_shm_simultaneous5".parse().unwrap(), + "shm/transport_shm_simultaneous6".parse().unwrap(), + "shm/transport_shm_simultaneous7".parse().unwrap(), + "shm/transport_shm_simultaneous8".parse().unwrap(), + ]; + + task::block_on(async { + transport_simultaneous(endpoint01, endpoint02).await; + }); + } + #[cfg(feature = "transport_ws")] #[test] + #[ignore] fn transport_ws_simultaneous() { let _ = env_logger::try_init(); task::block_on(async { diff --git a/io/zenoh-transport/tests/unicast_transport.rs b/io/zenoh-transport/tests/unicast_transport.rs index 0d786b61e5..25f9e8c530 100644 --- a/io/zenoh-transport/tests/unicast_transport.rs +++ b/io/zenoh-transport/tests/unicast_transport.rs @@ -22,14 +22,20 @@ use std::{ }, time::Duration, }; -use zenoh_buffers::ZBuf; use zenoh_core::zasync_executor_init; use zenoh_link::Link; use zenoh_protocol::{ - core::{Channel, CongestionControl, EndPoint, Priority, Reliability, WhatAmI, ZenohId}, - zenoh::ZenohMessage, + core::{ + Channel, CongestionControl, Encoding, EndPoint, Priority, Reliability, WhatAmI, ZenohId, + }, + network::{ + push::ext::{NodeIdType, QoSType}, + NetworkMessage, Push, + }, + zenoh::Put, }; use zenoh_result::ZResult; +use zenoh_transport::test_helpers::make_transport_manager_builder; use zenoh_transport::{ TransportEventHandler, TransportManager, TransportMulticast, TransportMulticastEventHandler, TransportPeer, TransportPeerEventHandler, TransportUnicast, @@ -215,6 +221,8 @@ const SLEEP_COUNT: Duration = Duration::from_millis(10); const MSG_COUNT: usize = 1_000; const MSG_SIZE_ALL: [usize; 2] = [1_024, 131_072]; +#[cfg(feature = "shared-memory")] +const MSG_SIZE_SHM: [usize; 2] = [1_024, 65000]; const MSG_SIZE_NOFRAG: [usize; 1] = [1_024]; macro_rules! ztimeout { @@ -272,7 +280,7 @@ impl SCRouter { } impl TransportPeerEventHandler for SCRouter { - fn handle_message(&self, _message: ZenohMessage) -> ZResult<()> { + fn handle_message(&self, _message: NetworkMessage) -> ZResult<()> { self.count.fetch_add(1, Ordering::SeqCst); Ok(()) } @@ -313,7 +321,7 @@ impl TransportEventHandler for SHClient { pub struct SCClient; impl TransportPeerEventHandler for SCClient { - fn handle_message(&self, _message: ZenohMessage) -> ZResult<()> { + fn handle_message(&self, _message: NetworkMessage) -> ZResult<()> { Ok(()) } @@ -327,9 +335,10 @@ impl TransportPeerEventHandler for SCClient { } } -async fn open_transport( +async fn open_transport_unicast( client_endpoints: &[EndPoint], server_endpoints: &[EndPoint], + #[cfg(feature = "shared-memory")] shm_transport: bool, ) -> ( TransportManager, Arc, @@ -342,8 +351,12 @@ async fn open_transport( // Create the router transport manager let router_handler = Arc::new(SHRouter::default()); - let unicast = TransportManager::config_unicast().max_links(server_endpoints.len()); - + let unicast = make_transport_manager_builder( + #[cfg(feature = "transport_multilink")] + server_endpoints.len(), + #[cfg(feature = "shared-memory")] + shm_transport, + ); let router_manager = TransportManager::builder() .zid(router_id) .whatami(WhatAmI::Router) @@ -353,12 +366,17 @@ async fn open_transport( // Create the listener on the router for e in server_endpoints.iter() { - println!("Add endpoint: {e}\n"); + println!("Add endpoint: {}", e); let _ = ztimeout!(router_manager.add_listener(e.clone())).unwrap(); } // Create the client transport manager - let unicast = TransportManager::config_unicast().max_links(client_endpoints.len()); + let unicast = make_transport_manager_builder( + #[cfg(feature = "transport_multilink")] + client_endpoints.len(), + #[cfg(feature = "shared-memory")] + shm_transport, + ); let client_manager = TransportManager::builder() .whatami(WhatAmI::Client) .zid(client_id) @@ -369,11 +387,14 @@ async fn open_transport( // Create an empty transport with the client // Open transport -> This should be accepted for e in client_endpoints.iter() { - println!("Opening transport with {e}"); - let _ = ztimeout!(client_manager.open_transport(e.clone())).unwrap(); + println!("Opening transport with {}", e); + let _ = ztimeout!(client_manager.open_transport_unicast(e.clone())).unwrap(); } - let client_transport = client_manager.get_transport(&router_id).unwrap(); + let client_transport = client_manager + .get_transport_unicast(&router_id) + .await + .unwrap(); // Return the handlers ( @@ -395,18 +416,18 @@ async fn close_transport( for e in endpoints.iter() { let _ = write!(ee, "{e} "); } - println!("Closing transport with {ee}"); + println!("Closing transport with {}", ee); ztimeout!(client_transport.close()).unwrap(); ztimeout!(async { - while !router_manager.get_transports().is_empty() { + while !router_manager.get_transports_unicast().await.is_empty() { task::sleep(SLEEP).await; } }); // Stop the locators on the manager for e in endpoints.iter() { - println!("Del locator: {e}"); + println!("Del locator: {}", e); ztimeout!(router_manager.del_listener(e)).unwrap(); } @@ -432,27 +453,37 @@ async fn test_transport( channel: Channel, msg_size: usize, ) { - // Create the message to send - let key = "test".into(); - let payload = ZBuf::from(vec![0_u8; msg_size]); - let data_info = None; - let routing_context = None; - let reply_context = None; - let attachment = None; - let message = ZenohMessage::make_data( - key, - payload, - channel, - CongestionControl::Block, - data_info, - routing_context, - reply_context, - attachment, + println!( + "Sending {} messages... {:?} {}", + MSG_COUNT, channel, msg_size ); - - println!("Sending {MSG_COUNT} messages... {channel:?} {msg_size}"); + let cctrl = match channel.reliability { + Reliability::Reliable => CongestionControl::Block, + Reliability::BestEffort => CongestionControl::Drop, + }; + // Create the message to send + let message: NetworkMessage = Push { + wire_expr: "test".into(), + ext_qos: QoSType::new(channel.priority, cctrl, false), + ext_tstamp: None, + ext_nodeid: NodeIdType::default(), + payload: Put { + payload: vec![0u8; msg_size].into(), + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + } + .into(), + } + .into(); for _ in 0..MSG_COUNT { - client_transport.schedule(message.clone()).unwrap(); + let _ = client_transport.schedule(message.clone()); + // print!("S-{i} "); + use std::io::Write; + std::io::stdout().flush().unwrap(); } match channel.reliability { @@ -481,10 +512,22 @@ async fn run_single( server_endpoints: &[EndPoint], channel: Channel, msg_size: usize, + #[cfg(feature = "shared-memory")] shm_transport: bool, ) { + println!( + "\n>>> Running test for: {:?}, {:?}, {:?}, {}", + client_endpoints, server_endpoints, channel, msg_size + ); + #[allow(unused_variables)] // Used when stats feature is enabled let (router_manager, router_handler, client_manager, client_transport) = - open_transport(client_endpoints, server_endpoints).await; + open_transport_unicast( + client_endpoints, + server_endpoints, + #[cfg(feature = "shared-memory")] + shm_transport, + ) + .await; test_transport( router_handler.clone(), @@ -496,14 +539,16 @@ async fn run_single( #[cfg(feature = "stats")] { - let c_stats = client_transport.get_stats().unwrap(); - println!("\tClient: {c_stats:?}"); + let c_stats = client_transport.get_stats().unwrap().report(); + println!("\tClient: {:?}", c_stats); let r_stats = router_manager .get_transport_unicast(&client_manager.config.zid) + .await .unwrap() .get_stats() + .map(|s| s.report()) .unwrap(); - println!("\tRouter: {r_stats:?}"); + println!("\tRouter: {:?}", r_stats); } close_transport( @@ -515,19 +560,59 @@ async fn run_single( .await; } -async fn run( +async fn run_internal( client_endpoints: &[EndPoint], server_endpoints: &[EndPoint], channel: &[Channel], msg_size: &[usize], + #[cfg(feature = "shared-memory")] shm_transport: bool, ) { for ch in channel.iter() { for ms in msg_size.iter() { - run_single(client_endpoints, server_endpoints, *ch, *ms).await; + run_single( + client_endpoints, + server_endpoints, + *ch, + *ms, + #[cfg(feature = "shared-memory")] + shm_transport, + ) + .await; } } } +async fn run_with_net( + client_endpoints: &[EndPoint], + server_endpoints: &[EndPoint], + channel: &[Channel], + msg_size: &[usize], +) { + run_internal( + client_endpoints, + server_endpoints, + channel, + msg_size, + #[cfg(feature = "shared-memory")] + false, + ) + .await; +} + +#[cfg(feature = "shared-memory")] +async fn run_with_shm( + client_endpoints: &[EndPoint], + server_endpoints: &[EndPoint], + channel: &[Channel], + msg_size: &[usize], +) { + if client_endpoints.len() > 1 || server_endpoints.len() > 1 { + println!("SHM transport doesn't support more than one link, so this test would produce MAX_LINKS error!"); + panic!(); + } + run_internal(client_endpoints, server_endpoints, channel, msg_size, true).await; +} + #[cfg(feature = "transport_tcp")] #[test] fn transport_unicast_tcp_only() { @@ -548,20 +633,47 @@ fn transport_unicast_tcp_only() { reliability: Reliability::Reliable, }, Channel { - priority: Priority::default(), - reliability: Reliability::BestEffort, + priority: Priority::RealTime, + reliability: Reliability::Reliable, }, + ]; + // Run + task::block_on(run_with_net( + &endpoints, + &endpoints, + &channel, + &MSG_SIZE_ALL, + )); +} + +#[cfg(all(feature = "transport_tcp", feature = "shared-memory",))] +#[test] +fn transport_unicast_tcp_only_with_shm() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + // Define the locators + let endpoints: Vec = vec![format!("tcp/127.0.0.1:{}", 16100).parse().unwrap()]; + // Define the reliability and congestion control + let channel = [ Channel { - priority: Priority::RealTime, + priority: Priority::default(), reliability: Reliability::Reliable, }, Channel { priority: Priority::RealTime, - reliability: Reliability::BestEffort, + reliability: Reliability::Reliable, }, ]; // Run - task::block_on(run(&endpoints, &endpoints, &channel, &MSG_SIZE_ALL)); + task::block_on(run_with_shm( + &endpoints, + &endpoints, + &channel, + &MSG_SIZE_SHM, + )); } #[cfg(feature = "transport_udp")] @@ -589,7 +701,42 @@ fn transport_unicast_udp_only() { }, ]; // Run - task::block_on(run(&endpoints, &endpoints, &channel, &MSG_SIZE_NOFRAG)); + task::block_on(run_with_net( + &endpoints, + &endpoints, + &channel, + &MSG_SIZE_NOFRAG, + )); +} + +#[cfg(all(feature = "transport_udp", feature = "shared-memory",))] +#[test] +fn transport_unicast_udp_only_with_shm() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + // Define the locator + let endpoints: Vec = vec![format!("udp/127.0.0.1:{}", 16110).parse().unwrap()]; + // Define the reliability and congestion control + let channel = [ + Channel { + priority: Priority::default(), + reliability: Reliability::BestEffort, + }, + Channel { + priority: Priority::RealTime, + reliability: Reliability::BestEffort, + }, + ]; + // Run + task::block_on(run_with_shm( + &endpoints, + &endpoints, + &channel, + &MSG_SIZE_NOFRAG, + )); } #[cfg(all(feature = "transport_unixsock-stream", target_family = "unix"))] @@ -616,7 +763,50 @@ fn transport_unicast_unix_only() { }, ]; // Run - task::block_on(run(&endpoints, &endpoints, &channel, &MSG_SIZE_ALL)); + task::block_on(run_with_net( + &endpoints, + &endpoints, + &channel, + &MSG_SIZE_ALL, + )); + let _ = std::fs::remove_file(f1); + let _ = std::fs::remove_file(format!("{f1}.lock")); +} + +#[cfg(all( + feature = "transport_unixsock-stream", + feature = "shared-memory", + target_family = "unix" +))] +#[test] +fn transport_unicast_unix_only_with_shm() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + let f1 = "zenoh-test-unix-socket-5-shm.sock"; + let _ = std::fs::remove_file(f1); + // Define the locator + let endpoints: Vec = vec![format!("unixsock-stream/{f1}").parse().unwrap()]; + // Define the reliability and congestion control + let channel = [ + Channel { + priority: Priority::default(), + reliability: Reliability::BestEffort, + }, + Channel { + priority: Priority::RealTime, + reliability: Reliability::BestEffort, + }, + ]; + // Run + task::block_on(run_with_shm( + &endpoints, + &endpoints, + &channel, + &MSG_SIZE_SHM, + )); let _ = std::fs::remove_file(f1); let _ = std::fs::remove_file(format!("{f1}.lock")); } @@ -654,7 +844,113 @@ fn transport_unicast_ws_only() { }, ]; // Run - task::block_on(run(&endpoints, &endpoints, &channel, &MSG_SIZE_ALL)); + task::block_on(run_with_net( + &endpoints, + &endpoints, + &channel, + &MSG_SIZE_ALL, + )); +} + +#[cfg(all(feature = "transport_ws", feature = "shared-memory",))] +#[test] +fn transport_unicast_ws_only_with_shm() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + // Define the locators + let endpoints: Vec = vec![format!("ws/127.0.0.1:{}", 16120).parse().unwrap()]; + // Define the reliability and congestion control + let channel = [ + Channel { + priority: Priority::default(), + reliability: Reliability::Reliable, + }, + Channel { + priority: Priority::default(), + reliability: Reliability::BestEffort, + }, + Channel { + priority: Priority::RealTime, + reliability: Reliability::Reliable, + }, + Channel { + priority: Priority::RealTime, + reliability: Reliability::BestEffort, + }, + ]; + // Run + task::block_on(run_with_shm( + &endpoints, + &endpoints, + &channel, + &MSG_SIZE_SHM, + )); +} + +#[cfg(feature = "transport_shm")] +#[test] +fn transport_unicast_shm_only() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + // Define the locator + let endpoints: Vec = vec![ + "shm/transport_unicast_shm_only".parse().unwrap(), + "shm/transport_unicast_shm_only2".parse().unwrap(), + ]; + // Define the reliability and congestion control + let channel = [ + Channel { + priority: Priority::default(), + reliability: Reliability::Reliable, + }, + Channel { + priority: Priority::RealTime, + reliability: Reliability::Reliable, + }, + ]; + // Run + task::block_on(run_with_net( + &endpoints, + &endpoints, + &channel, + &MSG_SIZE_ALL, + )); +} + +#[cfg(all(feature = "transport_shm", feature = "shared-memory",))] +#[test] +fn transport_unicast_shm_only_with_shm() { + let _ = env_logger::try_init(); + task::block_on(async { + zasync_executor_init!(); + }); + + // Define the locator + let endpoints: Vec = vec!["shm/transport_unicast_shm_only_with_shm".parse().unwrap()]; + // Define the reliability and congestion control + let channel = [ + Channel { + priority: Priority::default(), + reliability: Reliability::Reliable, + }, + Channel { + priority: Priority::RealTime, + reliability: Reliability::Reliable, + }, + ]; + // Run + task::block_on(run_with_shm( + &endpoints, + &endpoints, + &channel, + &MSG_SIZE_SHM, + )); } #[cfg(all(feature = "transport_tcp", feature = "transport_udp"))] @@ -684,7 +980,12 @@ fn transport_unicast_tcp_udp() { }, ]; // Run - task::block_on(run(&endpoints, &endpoints, &channel, &MSG_SIZE_NOFRAG)); + task::block_on(run_with_net( + &endpoints, + &endpoints, + &channel, + &MSG_SIZE_NOFRAG, + )); } #[cfg(all( @@ -719,7 +1020,12 @@ fn transport_unicast_tcp_unix() { }, ]; // Run - task::block_on(run(&endpoints, &endpoints, &channel, &MSG_SIZE_ALL)); + task::block_on(run_with_net( + &endpoints, + &endpoints, + &channel, + &MSG_SIZE_ALL, + )); let _ = std::fs::remove_file(f1); let _ = std::fs::remove_file(format!("{f1}.lock")); } @@ -756,7 +1062,12 @@ fn transport_unicast_udp_unix() { }, ]; // Run - task::block_on(run(&endpoints, &endpoints, &channel, &MSG_SIZE_NOFRAG)); + task::block_on(run_with_net( + &endpoints, + &endpoints, + &channel, + &MSG_SIZE_NOFRAG, + )); let _ = std::fs::remove_file(f1); let _ = std::fs::remove_file(format!("{f1}.lock")); } @@ -796,7 +1107,12 @@ fn transport_unicast_tcp_udp_unix() { }, ]; // Run - task::block_on(run(&endpoints, &endpoints, &channel, &MSG_SIZE_NOFRAG)); + task::block_on(run_with_net( + &endpoints, + &endpoints, + &channel, + &MSG_SIZE_NOFRAG, + )); let _ = std::fs::remove_file(f1); let _ = std::fs::remove_file(format!("{f1}.lock")); } @@ -847,7 +1163,12 @@ fn transport_unicast_tls_only_server() { ]; // Run let endpoints = vec![endpoint]; - task::block_on(run(&endpoints, &endpoints, &channel, &MSG_SIZE_ALL)); + task::block_on(run_with_net( + &endpoints, + &endpoints, + &channel, + &MSG_SIZE_ALL, + )); } #[cfg(feature = "transport_quic")] @@ -896,7 +1217,12 @@ fn transport_unicast_quic_only_server() { ]; // Run let endpoints = vec![endpoint]; - task::block_on(run(&endpoints, &endpoints, &channel, &MSG_SIZE_ALL)); + task::block_on(run_with_net( + &endpoints, + &endpoints, + &channel, + &MSG_SIZE_ALL, + )); } #[cfg(all(feature = "transport_tls", target_family = "unix"))] @@ -963,7 +1289,7 @@ fn transport_unicast_tls_only_mutual_success() { // Run let client_endpoints = vec![client_endpoint]; let server_endpoints = vec![server_endpoint]; - task::block_on(run( + task::block_on(run_with_net( &client_endpoints, &server_endpoints, &channel, @@ -1042,7 +1368,7 @@ fn transport_unicast_tls_only_mutual_no_client_certs_failure() { let client_endpoints = vec![client_endpoint]; let server_endpoints = vec![server_endpoint]; let result = std::panic::catch_unwind(|| { - task::block_on(run( + task::block_on(run_with_net( &client_endpoints, &server_endpoints, &channel, @@ -1124,7 +1450,7 @@ fn transport_unicast_tls_only_mutual_wrong_client_certs_failure() { let client_endpoints = vec![client_endpoint]; let server_endpoints = vec![server_endpoint]; let result = std::panic::catch_unwind(|| { - task::block_on(run( + task::block_on(run_with_net( &client_endpoints, &server_endpoints, &channel, diff --git a/plugins/zenoh-backend-traits/src/lib.rs b/plugins/zenoh-backend-traits/src/lib.rs index eacce6eb11..ac1e1c973c 100644 --- a/plugins/zenoh-backend-traits/src/lib.rs +++ b/plugins/zenoh-backend-traits/src/lib.rs @@ -28,7 +28,6 @@ //! use std::sync::Arc; //! use async_trait::async_trait; //! use zenoh::prelude::r#async::*; -//! use zenoh::properties::properties_to_json_value; //! use zenoh::time::Timestamp; //! use zenoh_backend_traits::*; //! use zenoh_backend_traits::config::*; diff --git a/plugins/zenoh-plugin-rest/Cargo.toml b/plugins/zenoh-plugin-rest/Cargo.toml index c3bcdc1e6d..f3d10b1618 100644 --- a/plugins/zenoh-plugin-rest/Cargo.toml +++ b/plugins/zenoh-plugin-rest/Cargo.toml @@ -48,7 +48,6 @@ serde = { workspace = true, features = ["default"] } serde_json = { workspace = true } tide = { workspace = true } zenoh = { workspace = true, features = ["unstable"] } -zenoh-cfg-properties = { workspace = true } zenoh-plugin-trait = { workspace = true } zenoh-result = { workspace = true } zenoh-util = { workspace = true } diff --git a/plugins/zenoh-plugin-rest/src/lib.rs b/plugins/zenoh-plugin-rest/src/lib.rs index 2ed0326708..3f6795505e 100644 --- a/plugins/zenoh-plugin-rest/src/lib.rs +++ b/plugins/zenoh-plugin-rest/src/lib.rs @@ -29,11 +29,11 @@ use tide::sse::Sender; use tide::{Request, Response, Server, StatusCode}; use zenoh::plugins::{Plugin, RunningPluginTrait, ZenohPlugin}; use zenoh::prelude::r#async::*; +use zenoh::properties::Properties; use zenoh::query::{QueryConsolidation, Reply}; use zenoh::runtime::Runtime; use zenoh::selector::TIME_RANGE_KEY; use zenoh::Session; -use zenoh_cfg_properties::Properties; use zenoh_result::{bail, zerror, ZResult}; mod config; diff --git a/plugins/zenoh-plugin-storage-manager/src/replica/storage.rs b/plugins/zenoh-plugin-storage-manager/src/replica/storage.rs index bd43b131f0..f486f25f3c 100644 --- a/plugins/zenoh-plugin-storage-manager/src/replica/storage.rs +++ b/plugins/zenoh-plugin-storage-manager/src/replica/storage.rs @@ -23,6 +23,7 @@ use std::str::{self, FromStr}; use std::time::{SystemTime, UNIX_EPOCH}; use zenoh::buffers::ZBuf; use zenoh::prelude::r#async::*; +use zenoh::query::ConsolidationMode; use zenoh::time::{Timestamp, NTP64}; use zenoh::{Result as ZResult, Session}; use zenoh_backend_traits::config::{GarbageCollectionConfig, StorageConfig}; diff --git a/zenoh/Cargo.toml b/zenoh/Cargo.toml index c12d50c5bc..e6d5c04f60 100644 --- a/zenoh/Cargo.toml +++ b/zenoh/Cargo.toml @@ -37,9 +37,11 @@ shared-memory = [ "zenoh-protocol/shared-memory", "zenoh-transport/shared-memory", ] -stats = ["zenoh-transport/stats"] +stats = ["zenoh-transport/stats", "zenoh-protocol/stats"] +transport_multilink = ["zenoh-transport/transport_multilink"] transport_quic = ["zenoh-transport/transport_quic"] transport_serial = ["zenoh-transport/transport_serial"] +transport_shm = ["zenoh-transport/transport_shm"] transport_tcp = ["zenoh-transport/transport_tcp"] transport_tls = ["zenoh-transport/transport_tls"] transport_udp = ["zenoh-transport/transport_udp"] @@ -49,6 +51,7 @@ unstable = [] default = [ "auth_pubkey", "auth_usrpwd", + "transport_multilink", "transport_quic", "transport_tcp", "transport_tls", @@ -72,6 +75,7 @@ hex = { workspace = true, features = ["default"] } lazy_static = { workspace = true } log = { workspace = true } ordered-float = { workspace = true } +paste = { workspace = true } petgraph = { workspace = true } rand = { workspace = true, features = ["default"] } regex = { workspace = true } @@ -83,9 +87,8 @@ uhlc = { workspace = true, features = ["default"] } uuid = { workspace = true, features = ["default"] } vec_map = { workspace = true } zenoh-buffers = { workspace = true, features = ["std"] } -zenoh-cfg-properties = { workspace = true } zenoh-codec = { workspace = true } -zenoh-collections = { workspace = true } +zenoh-collections = { workspace = true, features = ["std"] } zenoh-config = { workspace = true } zenoh-core = { workspace = true } zenoh-crypto = { workspace = true } diff --git a/zenoh/src/admin.rs b/zenoh/src/admin.rs index e30ad02974..56772797ce 100644 --- a/zenoh/src/admin.rs +++ b/zenoh/src/admin.rs @@ -1,9 +1,3 @@ -use std::{ - collections::hash_map::DefaultHasher, - hash::{Hash, Hasher}, - sync::Arc, -}; - // // Copyright (c) 2023 ZettaScale Technology // @@ -19,14 +13,21 @@ use std::{ // use crate::{ keyexpr, - prelude::sync::{KeyExpr, Locality}, + prelude::sync::{KeyExpr, Locality, SampleKind}, queryable::Query, + sample::DataInfo, Sample, Session, ZResult, }; +use async_std::task; +use std::{ + collections::hash_map::DefaultHasher, + hash::{Hash, Hasher}, + sync::Arc, +}; use zenoh_core::SyncResolve; use zenoh_protocol::{ - core::{Encoding, KnownEncoding, SampleKind, WireExpr}, - zenoh::{DataInfo, ZenohMessage}, + core::{Encoding, KnownEncoding, WireExpr}, + network::NetworkMessage, }; use zenoh_transport::{ TransportEventHandler, TransportMulticastEventHandler, TransportPeer, TransportPeerEventHandler, @@ -91,12 +92,12 @@ pub(crate) fn on_admin_query(session: &Session, query: Query) { } if let Ok(own_zid) = keyexpr::new(&session.zid().to_string()) { - for transport in session.runtime.manager().get_transports() { + for transport in task::block_on(session.runtime.manager().get_transports_unicast()) { if let Ok(peer) = transport.get_peer() { reply_peer(own_zid, &query, peer); } } - for transport in session.runtime.manager().get_transports_multicast() { + for transport in task::block_on(session.runtime.manager().get_transports_multicast()) { for peer in transport.get_peers().unwrap_or_default() { reply_peer(own_zid, &query, peer); } @@ -180,7 +181,7 @@ pub(crate) struct PeerHandler { } impl TransportPeerEventHandler for PeerHandler { - fn handle_message(&self, _msg: ZenohMessage) -> ZResult<()> { + fn handle_message(&self, _msg: NetworkMessage) -> ZResult<()> { Ok(()) } diff --git a/zenoh/src/info.rs b/zenoh/src/info.rs index ec9d510689..b8b3d0bd88 100644 --- a/zenoh/src/info.rs +++ b/zenoh/src/info.rs @@ -14,9 +14,10 @@ //! Tools to access information about the current zenoh [`Session`](crate::Session). use crate::SessionRef; +use async_std::task; use std::future::Ready; -use zenoh_config::{WhatAmI, ZenohId}; use zenoh_core::{AsyncResolve, Resolvable, SyncResolve}; +use zenoh_protocol::core::{WhatAmI, ZenohId}; /// A builder retuned by [`SessionInfo::zid()`](SessionInfo::zid) that allows /// to access the [`ZenohId`] of the current zenoh [`Session`](crate::Session). @@ -77,10 +78,7 @@ impl<'a> Resolvable for RoutersZidBuilder<'a> { impl<'a> SyncResolve for RoutersZidBuilder<'a> { fn res_sync(self) -> Self::To { Box::new( - self.session - .runtime - .manager() - .get_transports() + task::block_on(self.session.runtime.manager().get_transports_unicast()) .into_iter() .filter_map(|s| { s.get_whatami() @@ -125,10 +123,7 @@ impl<'a> Resolvable for PeersZidBuilder<'a> { impl<'a> SyncResolve for PeersZidBuilder<'a> { fn res_sync(self) -> ::To { Box::new( - self.session - .runtime - .manager() - .get_transports() + task::block_on(self.session.runtime.manager().get_transports_unicast()) .into_iter() .filter_map(|s| { s.get_whatami() diff --git a/zenoh/src/key_expr.rs b/zenoh/src/key_expr.rs index 7518c60d35..ad41c30457 100644 --- a/zenoh/src/key_expr.rs +++ b/zenoh/src/key_expr.rs @@ -21,7 +21,10 @@ use std::{ }; use zenoh_core::{AsyncResolve, Resolvable, SyncResolve}; pub use zenoh_protocol::core::key_expr::*; -use zenoh_protocol::core::{key_expr::canon::Canonizable, WireExpr}; +use zenoh_protocol::{ + core::{key_expr::canon::Canonizable, ExprId, WireExpr}, + network::{declare, DeclareBody, Mapping, UndeclareKeyExpr}, +}; use zenoh_result::ZResult; use zenoh_transport::Primitives; @@ -32,14 +35,16 @@ pub(crate) enum KeyExprInner<'a> { Borrowed(&'a keyexpr), BorrowedWire { key_expr: &'a keyexpr, - expr_id: u64, + expr_id: ExprId, + mapping: Mapping, prefix_len: u32, session_id: u16, }, Owned(OwnedKeyExpr), Wire { key_expr: OwnedKeyExpr, - expr_id: u64, + expr_id: ExprId, + mapping: Mapping, prefix_len: u32, session_id: u16, }, @@ -107,11 +112,13 @@ impl<'a> KeyExpr<'a> { KeyExprInner::BorrowedWire { key_expr, expr_id, + mapping, prefix_len, session_id, } => KeyExprInner::BorrowedWire { key_expr, expr_id: *expr_id, + mapping: *mapping, prefix_len: *prefix_len, session_id: *session_id, }, @@ -119,11 +126,13 @@ impl<'a> KeyExpr<'a> { KeyExprInner::Wire { key_expr, expr_id, + mapping, prefix_len, session_id, } => KeyExprInner::BorrowedWire { key_expr, expr_id: *expr_id, + mapping: *mapping, prefix_len: *prefix_len, session_id: *session_id, }, @@ -164,22 +173,26 @@ impl<'a> KeyExpr<'a> { KeyExprInner::BorrowedWire { key_expr, expr_id, + mapping, prefix_len, session_id, } => KeyExpr(KeyExprInner::Wire { key_expr: key_expr.into(), expr_id, + mapping, prefix_len, session_id, }), KeyExprInner::Wire { key_expr, expr_id, + mapping, prefix_len, session_id, } => KeyExpr(KeyExprInner::Wire { key_expr, expr_id, + mapping, prefix_len, session_id, }), @@ -202,6 +215,7 @@ impl<'a> KeyExpr<'a> { let r = self.as_keyexpr().join(s)?; if let KeyExprInner::Wire { expr_id, + mapping, prefix_len, session_id, .. @@ -210,6 +224,7 @@ impl<'a> KeyExpr<'a> { Ok(KeyExpr(KeyExprInner::Wire { key_expr: r, expr_id: *expr_id, + mapping: *mapping, prefix_len: *prefix_len, session_id: *session_id, })) @@ -233,12 +248,14 @@ impl<'a> KeyExpr<'a> { let r = OwnedKeyExpr::try_from(format!("{self}{s}"))?; if let KeyExprInner::Wire { expr_id, + mapping, prefix_len, session_id, .. } | KeyExprInner::BorrowedWire { expr_id, + mapping, prefix_len, session_id, .. @@ -247,6 +264,7 @@ impl<'a> KeyExpr<'a> { Ok(KeyExpr(KeyExprInner::Wire { key_expr: r, expr_id: *expr_id, + mapping: *mapping, prefix_len: *prefix_len, session_id: *session_id, })) @@ -318,11 +336,13 @@ impl<'a> From<&'a KeyExpr<'a>> for KeyExpr<'a> { KeyExprInner::BorrowedWire { key_expr, expr_id, + mapping, prefix_len, session_id, } => Self(KeyExprInner::BorrowedWire { key_expr, expr_id: *expr_id, + mapping: *mapping, prefix_len: *prefix_len, session_id: *session_id, }), @@ -330,11 +350,13 @@ impl<'a> From<&'a KeyExpr<'a>> for KeyExpr<'a> { KeyExprInner::Wire { key_expr, expr_id, + mapping, prefix_len, session_id, } => Self(KeyExprInner::BorrowedWire { key_expr, expr_id: *expr_id, + mapping: *mapping, prefix_len: *prefix_len, session_id: *session_id, }), @@ -417,11 +439,13 @@ impl std::ops::Div<&keyexpr> for KeyExpr<'_> { KeyExprInner::BorrowedWire { key_expr, expr_id, + mapping, prefix_len, session_id, } => KeyExpr(KeyExprInner::Wire { key_expr: key_expr / rhs, expr_id, + mapping, prefix_len, session_id, }), @@ -429,11 +453,13 @@ impl std::ops::Div<&keyexpr> for KeyExpr<'_> { KeyExprInner::Wire { key_expr, expr_id, + mapping, prefix_len, session_id, } => KeyExpr(KeyExprInner::Wire { key_expr: key_expr / rhs, expr_id, + mapping, prefix_len, session_id, }), @@ -449,11 +475,13 @@ impl std::ops::Div<&keyexpr> for &KeyExpr<'_> { KeyExprInner::BorrowedWire { key_expr, expr_id, + mapping, prefix_len, session_id, } => KeyExpr(KeyExprInner::Wire { key_expr: *key_expr / rhs, expr_id: *expr_id, + mapping: *mapping, prefix_len: *prefix_len, session_id: *session_id, }), @@ -461,11 +489,13 @@ impl std::ops::Div<&keyexpr> for &KeyExpr<'_> { KeyExprInner::Wire { key_expr, expr_id, + mapping, prefix_len, session_id, } => KeyExpr(KeyExprInner::Wire { key_expr: key_expr / rhs, expr_id: *expr_id, + mapping: *mapping, prefix_len: *prefix_len, session_id: *session_id, }), @@ -499,29 +529,35 @@ impl<'a> KeyExpr<'a> { KeyExprInner::Wire { key_expr, expr_id, + mapping, prefix_len, session_id, } if session.id == *session_id => WireExpr { scope: *expr_id, suffix: std::borrow::Cow::Borrowed(&key_expr.as_str()[((*prefix_len) as usize)..]), + mapping: *mapping, }, KeyExprInner::BorrowedWire { key_expr, expr_id, + mapping, prefix_len, session_id, } if session.id == *session_id => WireExpr { scope: *expr_id, suffix: std::borrow::Cow::Borrowed(&key_expr.as_str()[((*prefix_len) as usize)..]), + mapping: *mapping, }, KeyExprInner::Owned(key_expr) | KeyExprInner::Wire { key_expr, .. } => WireExpr { scope: 0, suffix: std::borrow::Cow::Borrowed(key_expr.as_str()), + mapping: Mapping::Sender, }, KeyExprInner::Borrowed(key_expr) | KeyExprInner::BorrowedWire { key_expr, .. } => { WireExpr { scope: 0, suffix: std::borrow::Cow::Borrowed(key_expr.as_str()), + mapping: Mapping::Sender, } } } @@ -566,7 +602,8 @@ impl SyncResolve for KeyExprUndeclaration<'_> { key_expr, expr_id, prefix_len, - session_id + session_id, + .. } if *prefix_len as usize == key_expr.len() => { if *session_id == session.id { *expr_id @@ -578,7 +615,8 @@ impl SyncResolve for KeyExprUndeclaration<'_> { key_expr, expr_id, prefix_len, - session_id + session_id, + .. } if *prefix_len as usize == key_expr.len() => { if *session_id == session.id { *expr_id @@ -594,7 +632,12 @@ impl SyncResolve for KeyExprUndeclaration<'_> { let primitives = state.primitives.as_ref().unwrap().clone(); drop(state); - primitives.forget_resource(expr_id); + primitives.send_declare(zenoh_protocol::network::Declare { + ext_qos: declare::ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: declare::ext::NodeIdType::default(), + body: DeclareBody::UndeclareKeyExpr(UndeclareKeyExpr { id: expr_id }), + }); Ok(()) } diff --git a/zenoh/src/lib.rs b/zenoh/src/lib.rs index f3b30a830d..82934a21bc 100644 --- a/zenoh/src/lib.rs +++ b/zenoh/src/lib.rs @@ -83,12 +83,12 @@ use git_version::git_version; use handlers::DefaultHandler; #[zenoh_macros::unstable] use net::runtime::Runtime; -use prelude::config::whatami::WhatAmIMatcher; use prelude::*; use scouting::ScoutBuilder; use std::future::Ready; use zenoh_core::{AsyncResolve, Resolvable, SyncResolve}; pub use zenoh_macros::{kedefine, keformat, kewrite}; +use zenoh_protocol::core::WhatAmIMatcher; use zenoh_result::{zerror, ZResult}; /// A zenoh error. @@ -148,7 +148,7 @@ pub mod time { /// A map of key/value (String,String) properties. pub mod properties { use super::prelude::Value; - pub use zenoh_cfg_properties::Properties; + pub use zenoh_collections::Properties; /// Convert a set of [`Properties`] into a [`Value`]. /// For instance, Properties: `[("k1", "v1"), ("k2, v2")]` @@ -174,7 +174,7 @@ pub mod scouting; /// # Arguments /// /// * `what` - The kind of zenoh process to scout for -/// * `config` - The configuration [`Properties`](crate::properties::Properties) to use for scouting +/// * `config` - The configuration [`Config`](crate::config::Config) to use for scouting /// /// # Examples /// ```no_run diff --git a/zenoh/src/liveliness.rs b/zenoh/src/liveliness.rs index 72fe7d814e..be39e3b7f6 100644 --- a/zenoh/src/liveliness.rs +++ b/zenoh/src/liveliness.rs @@ -36,7 +36,7 @@ use { zenoh_core::Resolvable, zenoh_core::Result as ZResult, zenoh_core::SyncResolve, - zenoh_protocol::core::SubInfo, + zenoh_protocol::network::declare::subscriber::ext::SubscriberInfo, }; #[zenoh_macros::unstable] @@ -544,7 +544,7 @@ where &Some(KeyExpr::from(*KE_PREFIX_LIVELINESS)), Locality::default(), callback, - &SubInfo::default(), + &SubscriberInfo::default(), ) .map(|sub_state| Subscriber { subscriber: SubscriberInner { diff --git a/zenoh/src/net/codec/linkstate.rs b/zenoh/src/net/codec/linkstate.rs new file mode 100644 index 0000000000..4954062a3d --- /dev/null +++ b/zenoh/src/net/codec/linkstate.rs @@ -0,0 +1,158 @@ +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use super::Zenoh080Routing; +use crate::net::protocol::{ + linkstate, + linkstate::{LinkState, LinkStateList}, +}; +use core::convert::TryFrom; +use zenoh_buffers::{ + reader::{DidntRead, Reader}, + writer::{DidntWrite, Writer}, +}; +use zenoh_codec::{RCodec, WCodec, Zenoh080}; +use zenoh_protocol::{ + common::imsg, + core::{Locator, WhatAmI, ZenohId}, +}; + +// LinkState +impl WCodec<&LinkState, &mut W> for Zenoh080Routing +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &LinkState) -> Self::Output { + let codec = Zenoh080::new(); + // Options + let mut options = 0; + if x.zid.is_some() { + options |= linkstate::PID; + } + if x.whatami.is_some() { + options |= linkstate::WAI; + } + if x.locators.is_some() { + options |= linkstate::LOC; + } + codec.write(&mut *writer, options)?; + + // Body + codec.write(&mut *writer, x.psid)?; + codec.write(&mut *writer, x.sn)?; + if let Some(zid) = x.zid.as_ref() { + codec.write(&mut *writer, zid)?; + } + if let Some(wai) = x.whatami { + let wai: u8 = wai.into(); + codec.write(&mut *writer, wai)?; + } + if let Some(locators) = x.locators.as_ref() { + codec.write(&mut *writer, locators.as_slice())?; + } + codec.write(&mut *writer, x.links.len())?; + for l in x.links.iter() { + codec.write(&mut *writer, *l)?; + } + + Ok(()) + } +} + +impl RCodec for Zenoh080Routing +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let codec = Zenoh080::new(); + let options: u64 = codec.read(&mut *reader)?; + let psid: u64 = codec.read(&mut *reader)?; + let sn: u64 = codec.read(&mut *reader)?; + let zid = if imsg::has_option(options, linkstate::PID) { + let zid: ZenohId = codec.read(&mut *reader)?; + Some(zid) + } else { + None + }; + let whatami = if imsg::has_option(options, linkstate::WAI) { + let wai: u8 = codec.read(&mut *reader)?; + Some(WhatAmI::try_from(wai).map_err(|_| DidntRead)?) + } else { + None + }; + let locators = if imsg::has_option(options, linkstate::LOC) { + let locs: Vec = codec.read(&mut *reader)?; + Some(locs) + } else { + None + }; + let len: usize = codec.read(&mut *reader)?; + let mut links: Vec = Vec::with_capacity(len); + for _ in 0..len { + let l: u64 = codec.read(&mut *reader)?; + links.push(l); + } + + Ok(LinkState { + psid, + sn, + zid, + whatami, + locators, + links, + }) + } +} + +// LinkStateList +impl WCodec<&LinkStateList, &mut W> for Zenoh080Routing +where + W: Writer, +{ + type Output = Result<(), DidntWrite>; + + fn write(self, writer: &mut W, x: &LinkStateList) -> Self::Output { + let codec = Zenoh080::new(); + + codec.write(&mut *writer, x.link_states.len())?; + for ls in x.link_states.iter() { + self.write(&mut *writer, ls)?; + } + + Ok(()) + } +} + +impl RCodec for Zenoh080Routing +where + R: Reader, +{ + type Error = DidntRead; + + fn read(self, reader: &mut R) -> Result { + let codec = Zenoh080::new(); + + let len: usize = codec.read(&mut *reader)?; + let mut link_states = Vec::with_capacity(len); + for _ in 0..len { + let ls: LinkState = self.read(&mut *reader)?; + link_states.push(ls); + } + + Ok(LinkStateList { link_states }) + } +} diff --git a/zenoh/src/net/codec/mod.rs b/zenoh/src/net/codec/mod.rs new file mode 100644 index 0000000000..06154cd02c --- /dev/null +++ b/zenoh/src/net/codec/mod.rs @@ -0,0 +1,23 @@ +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +pub(crate) mod linkstate; + +#[derive(Clone, Copy)] +pub struct Zenoh080Routing; + +impl Zenoh080Routing { + pub const fn new() -> Self { + Self + } +} diff --git a/zenoh/src/net/mod.rs b/zenoh/src/net/mod.rs index da063eb750..b0b4be3f14 100644 --- a/zenoh/src/net/mod.rs +++ b/zenoh/src/net/mod.rs @@ -18,6 +18,10 @@ //! //! [Click here for Zenoh's documentation](../zenoh/index.html) #[doc(hidden)] +pub(crate) mod codec; +#[doc(hidden)] +pub(crate) mod protocol; +#[doc(hidden)] pub(crate) mod routing; #[doc(hidden)] pub mod runtime; diff --git a/commons/zenoh-protocol/src/zenoh/linkstate.rs b/zenoh/src/net/protocol/linkstate.rs similarity index 79% rename from commons/zenoh-protocol/src/zenoh/linkstate.rs rename to zenoh/src/net/protocol/linkstate.rs index cbd365fe1e..ccb5612011 100644 --- a/commons/zenoh-protocol/src/zenoh/linkstate.rs +++ b/zenoh/src/net/protocol/linkstate.rs @@ -11,8 +11,11 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::core::{Locator, WhatAmI, ZInt, ZenohId}; -use alloc::vec::Vec; +use zenoh_protocol::core::{Locator, WhatAmI, ZenohId}; + +pub const PID: u64 = 1; // 0x01 +pub const WAI: u64 = 1 << 1; // 0x02 +pub const LOC: u64 = 1 << 2; // 0x04 // 7 6 5 4 3 2 1 0 // +-+-+-+-+-+-+-+-+ @@ -31,13 +34,13 @@ use alloc::vec::Vec; // ~ [links] ~ // +---------------+ #[derive(Debug, Clone, PartialEq, Eq)] -pub struct LinkState { - pub psid: ZInt, - pub sn: ZInt, - pub zid: Option, - pub whatami: Option, - pub locators: Option>, - pub links: Vec, +pub(crate) struct LinkState { + pub(crate) psid: u64, + pub(crate) sn: u64, + pub(crate) zid: Option, + pub(crate) whatami: Option, + pub(crate) locators: Option>, + pub(crate) links: Vec, } impl LinkState { @@ -50,8 +53,8 @@ impl LinkState { let mut rng = rand::thread_rng(); - let psid: ZInt = rng.gen(); - let sn: ZInt = rng.gen(); + let psid: u64 = rng.gen(); + let sn: u64 = rng.gen(); let zid = if rng.gen_bool(0.5) { Some(ZenohId::default()) } else { @@ -70,7 +73,7 @@ impl LinkState { None }; let n = rng.gen_range(MIN..=MAX); - let links = (0..n).map(|_| rng.gen()).collect::>(); + let links = (0..n).map(|_| rng.gen()).collect::>(); Self { psid, @@ -90,8 +93,8 @@ impl LinkState { // ~ [link_states] ~ // +---------------+ #[derive(Debug, Clone, PartialEq, Eq)] -pub struct LinkStateList { - pub link_states: Vec, +pub(crate) struct LinkStateList { + pub(crate) link_states: Vec, } impl LinkStateList { diff --git a/zenoh/src/net/protocol/mod.rs b/zenoh/src/net/protocol/mod.rs new file mode 100644 index 0000000000..7343a07fd0 --- /dev/null +++ b/zenoh/src/net/protocol/mod.rs @@ -0,0 +1,14 @@ +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +pub(crate) mod linkstate; diff --git a/zenoh/src/net/routing/face.rs b/zenoh/src/net/routing/face.rs index 2ea7617e96..d84f173d26 100644 --- a/zenoh/src/net/routing/face.rs +++ b/zenoh/src/net/routing/face.rs @@ -15,30 +15,34 @@ use super::router::*; use std::collections::{HashMap, HashSet}; use std::fmt; use std::sync::Arc; -use zenoh_buffers::ZBuf; +use zenoh_protocol::zenoh::RequestBody; use zenoh_protocol::{ - core::{ - Channel, CongestionControl, ConsolidationMode, QueryTarget, QueryableInfo, SubInfo, - WhatAmI, WireExpr, ZInt, ZenohId, + core::{ExprId, WhatAmI, ZenohId}, + network::{ + declare::queryable::ext::QueryableInfo, Mapping, Push, Request, RequestId, Response, + ResponseFinal, }, - zenoh::{DataInfo, QueryBody, RoutingContext}, }; +#[cfg(feature = "stats")] +use zenoh_transport::stats::TransportStats; use zenoh_transport::{Primitives, TransportMulticast}; pub struct FaceState { pub(super) id: usize, pub(super) zid: ZenohId, pub(super) whatami: WhatAmI, + #[cfg(feature = "stats")] + pub(super) stats: Option>, pub(super) primitives: Arc, pub(super) link_id: usize, - pub(super) local_mappings: HashMap>, - pub(super) remote_mappings: HashMap>, + pub(super) local_mappings: HashMap>, + pub(super) remote_mappings: HashMap>, pub(super) local_subs: HashSet>, pub(super) remote_subs: HashSet>, pub(super) local_qabls: HashMap, QueryableInfo>, pub(super) remote_qabls: HashSet>, - pub(super) next_qid: ZInt, - pub(super) pending_queries: HashMap>, + pub(super) next_qid: RequestId, + pub(super) pending_queries: HashMap>, pub(super) mcast_group: Option, } @@ -47,6 +51,7 @@ impl FaceState { id: usize, zid: ZenohId, whatami: WhatAmI, + #[cfg(feature = "stats")] stats: Option>, primitives: Arc, link_id: usize, mcast_group: Option, @@ -55,6 +60,8 @@ impl FaceState { id, zid, whatami, + #[cfg(feature = "stats")] + stats, primitives, link_id, local_mappings: HashMap::new(), @@ -71,14 +78,18 @@ impl FaceState { #[inline] #[allow(clippy::trivially_copy_pass_by_ref)] - pub(super) fn get_mapping(&self, prefixid: &ZInt) -> Option<&std::sync::Arc> { - match self.remote_mappings.get(prefixid) { - Some(prefix) => Some(prefix), - None => self.local_mappings.get(prefixid), + pub(super) fn get_mapping( + &self, + prefixid: &ExprId, + mapping: Mapping, + ) -> Option<&std::sync::Arc> { + match mapping { + Mapping::Sender => self.remote_mappings.get(prefixid), + Mapping::Receiver => self.local_mappings.get(prefixid), } } - pub(super) fn get_next_local_id(&self) -> ZInt { + pub(super) fn get_next_local_id(&self) -> ExprId { let mut id = 1; while self.local_mappings.get(&id).is_some() || self.remote_mappings.get(&id).is_some() { id += 1; @@ -86,69 +97,45 @@ impl FaceState { id } - pub(super) fn get_router( - &self, - tables: &Tables, - routing_context: Option, - ) -> Option { - match routing_context { - Some(routing_context) => { - match tables.routers_net.as_ref().unwrap().get_link(self.link_id) { - Some(link) => match link.get_zid(&routing_context.tree_id) { - Some(router) => Some(*router), - None => { - log::error!( - "Received router declaration with unknown routing context id {}", - routing_context.tree_id - ); - None - } - }, - None => { - log::error!( - "Could not find corresponding link in routers network for {}", - self - ); - None - } + pub(super) fn get_router(&self, tables: &Tables, nodeid: &u64) -> Option { + match tables.routers_net.as_ref().unwrap().get_link(self.link_id) { + Some(link) => match link.get_zid(nodeid) { + Some(router) => Some(*router), + None => { + log::error!( + "Received router declaration with unknown routing context id {}", + nodeid + ); + None } - } + }, None => { - log::error!("Received router declaration with no routing context"); + log::error!( + "Could not find corresponding link in routers network for {}", + self + ); None } } } - pub(super) fn get_peer( - &self, - tables: &Tables, - routing_context: Option, - ) -> Option { - match routing_context { - Some(routing_context) => { - match tables.peers_net.as_ref().unwrap().get_link(self.link_id) { - Some(link) => match link.get_zid(&routing_context.tree_id) { - Some(router) => Some(*router), - None => { - log::error!( - "Received peer declaration with unknown routing context id {}", - routing_context.tree_id - ); - None - } - }, - None => { - log::error!( - "Could not find corresponding link in peers network for {}", - self - ); - None - } + pub(super) fn get_peer(&self, tables: &Tables, nodeid: &u64) -> Option { + match tables.peers_net.as_ref().unwrap().get_link(self.link_id) { + Some(link) => match link.get_zid(nodeid) { + Some(router) => Some(*router), + None => { + log::error!( + "Received peer declaration with unknown routing context id {}", + nodeid + ); + None } - } + }, None => { - log::error!("Received peer declaration with no routing context"); + log::error!( + "Could not find corresponding link in peers network for {}", + self + ); None } } @@ -168,304 +155,280 @@ pub struct Face { } impl Primitives for Face { - fn decl_resource(&self, expr_id: ZInt, key_expr: &WireExpr) { + fn send_declare(&self, msg: zenoh_protocol::network::Declare) { let ctrl_lock = zlock!(self.tables.ctrl_lock); - register_expr(&self.tables, &mut self.state.clone(), expr_id, key_expr); - drop(ctrl_lock); - } - - fn forget_resource(&self, expr_id: ZInt) { - let ctrl_lock = zlock!(self.tables.ctrl_lock); - unregister_expr(&self.tables, &mut self.state.clone(), expr_id); - drop(ctrl_lock); - } - - fn decl_subscriber( - &self, - key_expr: &WireExpr, - sub_info: &SubInfo, - routing_context: Option, - ) { - let ctrl_lock = zlock!(self.tables.ctrl_lock); - let rtables = zread!(self.tables.tables); - match (rtables.whatami, self.state.whatami) { - (WhatAmI::Router, WhatAmI::Router) => { - if let Some(router) = self.state.get_router(&rtables, routing_context) { - declare_router_subscription( - &self.tables, - rtables, - &mut self.state.clone(), - key_expr, - sub_info, - router, - ); - } - } - (WhatAmI::Router, WhatAmI::Peer) - | (WhatAmI::Peer, WhatAmI::Router) - | (WhatAmI::Peer, WhatAmI::Peer) => { - if rtables.full_net(WhatAmI::Peer) { - if let Some(peer) = self.state.get_peer(&rtables, routing_context) { - declare_peer_subscription( - &self.tables, - rtables, - &mut self.state.clone(), - key_expr, - sub_info, - peer, - ); - } - } else { - declare_client_subscription( - &self.tables, - rtables, - &mut self.state.clone(), - key_expr, - sub_info, - ); - } - } - _ => { - declare_client_subscription( - &self.tables, - rtables, - &mut self.state.clone(), - key_expr, - sub_info, - ); + match msg.body { + zenoh_protocol::network::DeclareBody::DeclareKeyExpr(m) => { + register_expr(&self.tables, &mut self.state.clone(), m.id, &m.wire_expr); } - } - drop(ctrl_lock); - } - - fn forget_subscriber(&self, key_expr: &WireExpr, routing_context: Option) { - let ctrl_lock = zlock!(self.tables.ctrl_lock); - let rtables = zread!(self.tables.tables); - match (rtables.whatami, self.state.whatami) { - (WhatAmI::Router, WhatAmI::Router) => { - if let Some(router) = self.state.get_router(&rtables, routing_context) { - forget_router_subscription( - &self.tables, - rtables, - &mut self.state.clone(), - key_expr, - &router, - ) - } + zenoh_protocol::network::DeclareBody::UndeclareKeyExpr(m) => { + unregister_expr(&self.tables, &mut self.state.clone(), m.id); } - (WhatAmI::Router, WhatAmI::Peer) - | (WhatAmI::Peer, WhatAmI::Router) - | (WhatAmI::Peer, WhatAmI::Peer) => { - if rtables.full_net(WhatAmI::Peer) { - if let Some(peer) = self.state.get_peer(&rtables, routing_context) { - forget_peer_subscription( - &self.tables, - rtables, - &mut self.state.clone(), - key_expr, - &peer, - ) + zenoh_protocol::network::DeclareBody::DeclareSubscriber(m) => { + let rtables = zread!(self.tables.tables); + match (rtables.whatami, self.state.whatami) { + (WhatAmI::Router, WhatAmI::Router) => { + if let Some(router) = self + .state + .get_router(&rtables, &(msg.ext_nodeid.node_id as u64)) + { + declare_router_subscription( + &self.tables, + rtables, + &mut self.state.clone(), + &m.wire_expr, + &m.ext_info, + router, + ) + } } - } else { - forget_client_subscription( - &self.tables, - rtables, - &mut self.state.clone(), - key_expr, - ) - } - } - _ => { - forget_client_subscription(&self.tables, rtables, &mut self.state.clone(), key_expr) - } - } - drop(ctrl_lock); - } - - fn decl_publisher(&self, _key_expr: &WireExpr, _routing_context: Option) {} - - fn forget_publisher(&self, _key_expr: &WireExpr, _routing_context: Option) {} - - fn decl_queryable( - &self, - key_expr: &WireExpr, - qabl_info: &QueryableInfo, - routing_context: Option, - ) { - let ctrl_lock = zlock!(self.tables.ctrl_lock); - let rtables = zread!(self.tables.tables); - match (rtables.whatami, self.state.whatami) { - (WhatAmI::Router, WhatAmI::Router) => { - if let Some(router) = self.state.get_router(&rtables, routing_context) { - declare_router_queryable( + (WhatAmI::Router, WhatAmI::Peer) + | (WhatAmI::Peer, WhatAmI::Router) + | (WhatAmI::Peer, WhatAmI::Peer) => { + if rtables.full_net(WhatAmI::Peer) { + if let Some(peer) = self + .state + .get_peer(&rtables, &(msg.ext_nodeid.node_id as u64)) + { + declare_peer_subscription( + &self.tables, + rtables, + &mut self.state.clone(), + &m.wire_expr, + &m.ext_info, + peer, + ) + } + } else { + declare_client_subscription( + &self.tables, + rtables, + &mut self.state.clone(), + &m.wire_expr, + &m.ext_info, + ) + } + } + _ => declare_client_subscription( &self.tables, rtables, &mut self.state.clone(), - key_expr, - qabl_info, - router, - ) + &m.wire_expr, + &m.ext_info, + ), } } - (WhatAmI::Router, WhatAmI::Peer) - | (WhatAmI::Peer, WhatAmI::Router) - | (WhatAmI::Peer, WhatAmI::Peer) => { - if rtables.full_net(WhatAmI::Peer) { - if let Some(peer) = self.state.get_peer(&rtables, routing_context) { - declare_peer_queryable( - &self.tables, - rtables, - &mut self.state.clone(), - key_expr, - qabl_info, - peer, - ) + zenoh_protocol::network::DeclareBody::UndeclareSubscriber(m) => { + let rtables = zread!(self.tables.tables); + match (rtables.whatami, self.state.whatami) { + (WhatAmI::Router, WhatAmI::Router) => { + if let Some(router) = self + .state + .get_router(&rtables, &(msg.ext_nodeid.node_id as u64)) + { + forget_router_subscription( + &self.tables, + rtables, + &mut self.state.clone(), + &m.ext_wire_expr.wire_expr, + &router, + ) + } + } + (WhatAmI::Router, WhatAmI::Peer) + | (WhatAmI::Peer, WhatAmI::Router) + | (WhatAmI::Peer, WhatAmI::Peer) => { + if rtables.full_net(WhatAmI::Peer) { + if let Some(peer) = self + .state + .get_peer(&rtables, &(msg.ext_nodeid.node_id as u64)) + { + forget_peer_subscription( + &self.tables, + rtables, + &mut self.state.clone(), + &m.ext_wire_expr.wire_expr, + &peer, + ) + } + } else { + forget_client_subscription( + &self.tables, + rtables, + &mut self.state.clone(), + &m.ext_wire_expr.wire_expr, + ) + } } - } else { - declare_client_queryable( + _ => forget_client_subscription( &self.tables, rtables, &mut self.state.clone(), - key_expr, - qabl_info, - ) + &m.ext_wire_expr.wire_expr, + ), } } - _ => declare_client_queryable( - &self.tables, - rtables, - &mut self.state.clone(), - key_expr, - qabl_info, - ), - } - drop(ctrl_lock); - } - - fn forget_queryable(&self, key_expr: &WireExpr, routing_context: Option) { - let ctrl_lock = zlock!(self.tables.ctrl_lock); - let rtables = zread!(self.tables.tables); - match (rtables.whatami, self.state.whatami) { - (WhatAmI::Router, WhatAmI::Router) => { - if let Some(router) = self.state.get_router(&rtables, routing_context) { - forget_router_queryable( + zenoh_protocol::network::DeclareBody::DeclareQueryable(m) => { + let rtables = zread!(self.tables.tables); + match (rtables.whatami, self.state.whatami) { + (WhatAmI::Router, WhatAmI::Router) => { + if let Some(router) = self + .state + .get_router(&rtables, &(msg.ext_nodeid.node_id as u64)) + { + declare_router_queryable( + &self.tables, + rtables, + &mut self.state.clone(), + &m.wire_expr, + &m.ext_info, + router, + ) + } + } + (WhatAmI::Router, WhatAmI::Peer) + | (WhatAmI::Peer, WhatAmI::Router) + | (WhatAmI::Peer, WhatAmI::Peer) => { + if rtables.full_net(WhatAmI::Peer) { + if let Some(peer) = self + .state + .get_peer(&rtables, &(msg.ext_nodeid.node_id as u64)) + { + declare_peer_queryable( + &self.tables, + rtables, + &mut self.state.clone(), + &m.wire_expr, + &m.ext_info, + peer, + ) + } + } else { + declare_client_queryable( + &self.tables, + rtables, + &mut self.state.clone(), + &m.wire_expr, + &m.ext_info, + ) + } + } + _ => declare_client_queryable( &self.tables, rtables, &mut self.state.clone(), - key_expr, - &router, - ) + &m.wire_expr, + &m.ext_info, + ), } } - (WhatAmI::Router, WhatAmI::Peer) - | (WhatAmI::Peer, WhatAmI::Router) - | (WhatAmI::Peer, WhatAmI::Peer) => { - if rtables.full_net(WhatAmI::Peer) { - if let Some(peer) = self.state.get_peer(&rtables, routing_context) { - forget_peer_queryable( - &self.tables, - rtables, - &mut self.state.clone(), - key_expr, - &peer, - ) + zenoh_protocol::network::DeclareBody::UndeclareQueryable(m) => { + let rtables = zread!(self.tables.tables); + match (rtables.whatami, self.state.whatami) { + (WhatAmI::Router, WhatAmI::Router) => { + if let Some(router) = self + .state + .get_router(&rtables, &(msg.ext_nodeid.node_id as u64)) + { + forget_router_queryable( + &self.tables, + rtables, + &mut self.state.clone(), + &m.ext_wire_expr.wire_expr, + &router, + ) + } + } + (WhatAmI::Router, WhatAmI::Peer) + | (WhatAmI::Peer, WhatAmI::Router) + | (WhatAmI::Peer, WhatAmI::Peer) => { + if rtables.full_net(WhatAmI::Peer) { + if let Some(peer) = self + .state + .get_peer(&rtables, &(msg.ext_nodeid.node_id as u64)) + { + forget_peer_queryable( + &self.tables, + rtables, + &mut self.state.clone(), + &m.ext_wire_expr.wire_expr, + &peer, + ) + } + } else { + forget_client_queryable( + &self.tables, + rtables, + &mut self.state.clone(), + &m.ext_wire_expr.wire_expr, + ) + } } - } else { - forget_client_queryable( + _ => forget_client_queryable( &self.tables, rtables, &mut self.state.clone(), - key_expr, - ) + &m.ext_wire_expr.wire_expr, + ), } } - _ => forget_client_queryable(&self.tables, rtables, &mut self.state.clone(), key_expr), + zenoh_protocol::network::DeclareBody::DeclareToken(_m) => todo!(), + zenoh_protocol::network::DeclareBody::UndeclareToken(_m) => todo!(), + zenoh_protocol::network::DeclareBody::DeclareInterest(_m) => todo!(), + zenoh_protocol::network::DeclareBody::FinalInterest(_m) => todo!(), + zenoh_protocol::network::DeclareBody::UndeclareInterest(_m) => todo!(), } drop(ctrl_lock); } - fn send_data( - &self, - key_expr: &WireExpr, - payload: ZBuf, - channel: Channel, - congestion_control: CongestionControl, - data_info: Option, - routing_context: Option, - ) { + fn send_push(&self, msg: Push) { full_reentrant_route_data( &self.tables.tables, &self.state, - key_expr, - channel, - congestion_control, - data_info, - payload, - routing_context, + &msg.wire_expr, + msg.ext_qos, + msg.payload, + msg.ext_nodeid.node_id as u64, ); } - fn send_query( - &self, - key_expr: &WireExpr, - parameters: &str, - qid: ZInt, - target: QueryTarget, - consolidation: ConsolidationMode, - body: Option, - routing_context: Option, - ) { - route_query( - &self.tables, - &self.state, - key_expr, - parameters, - qid, - target, - consolidation, - body, - routing_context, - ); + fn send_request(&self, msg: Request) { + match msg.payload { + RequestBody::Query(_) => { + route_query( + &self.tables, + &self.state, + &msg.wire_expr, + // parameters, + msg.id, + msg.ext_target, + // consolidation, + msg.payload, + msg.ext_nodeid.node_id as u64, + ); + } + RequestBody::Pull(_) => { + pull_data(&self.tables.tables, &self.state.clone(), msg.wire_expr); + } + _ => { + log::error!("Unsupported request"); + } + } } - fn send_reply_data( - &self, - qid: ZInt, - replier_id: ZenohId, - key_expr: WireExpr, - info: Option, - payload: ZBuf, - ) { - route_send_reply_data( + fn send_response(&self, msg: Response) { + route_send_response( &self.tables, &mut self.state.clone(), - qid, - replier_id, - key_expr, - info, - payload, + msg.rid, + msg.ext_respid, + msg.wire_expr, + msg.payload, ); } - fn send_reply_final(&self, qid: ZInt) { - route_send_reply_final(&self.tables, &mut self.state.clone(), qid); - } - - fn send_pull( - &self, - is_final: bool, - key_expr: &WireExpr, - pull_id: ZInt, - max_samples: &Option, - ) { - pull_data( - &self.tables.tables, - &self.state.clone(), - is_final, - key_expr, - pull_id, - max_samples, - ); + fn send_response_final(&self, msg: ResponseFinal) { + route_send_response_final(&self.tables, &mut self.state.clone(), msg.rid); } fn send_close(&self) { diff --git a/zenoh/src/net/routing/network.rs b/zenoh/src/net/routing/network.rs index f72667b332..3af1e0a87c 100644 --- a/zenoh/src/net/routing/network.rs +++ b/zenoh/src/net/routing/network.rs @@ -11,17 +11,22 @@ // Contributors: // ZettaScale Zenoh Team, // -use super::runtime::Runtime; +use crate::net::codec::Zenoh080Routing; +use crate::net::protocol::linkstate::{LinkState, LinkStateList}; +use crate::net::runtime::Runtime; +use async_std::task; use petgraph::graph::NodeIndex; use petgraph::visit::{IntoNodeReferences, VisitMap, Visitable}; use std::convert::TryInto; use vec_map::VecMap; -use zenoh_config::whatami::WhatAmIMatcher; +use zenoh_buffers::writer::{DidntWrite, HasWriter}; +use zenoh_buffers::ZBuf; +use zenoh_codec::WCodec; use zenoh_link::Locator; -use zenoh_protocol::{ - core::{WhatAmI, ZInt, ZenohId}, - zenoh::{LinkState, ZenohMessage}, -}; +use zenoh_protocol::common::ZExtBody; +use zenoh_protocol::core::{WhatAmI, WhatAmIMatcher, ZenohId}; +use zenoh_protocol::network::oam::id::OAM_LINKSTATE; +use zenoh_protocol::network::{oam, NetworkBody, NetworkMessage, Oam}; use zenoh_transport::TransportUnicast; #[derive(Clone)] @@ -36,7 +41,7 @@ pub(crate) struct Node { pub(crate) zid: ZenohId, pub(crate) whatami: Option, pub(crate) locators: Option>, - pub(crate) sn: ZInt, + pub(crate) sn: u64, pub(crate) links: Vec, } @@ -50,7 +55,7 @@ pub(crate) struct Link { pub(crate) transport: TransportUnicast, zid: ZenohId, mappings: VecMap, - local_mappings: VecMap, + local_mappings: VecMap, } impl Link { @@ -65,23 +70,23 @@ impl Link { } #[inline] - pub(crate) fn set_zid_mapping(&mut self, psid: ZInt, zid: ZenohId) { + pub(crate) fn set_zid_mapping(&mut self, psid: u64, zid: ZenohId) { self.mappings.insert(psid.try_into().unwrap(), zid); } #[inline] - pub(crate) fn get_zid(&self, psid: &ZInt) -> Option<&ZenohId> { + pub(crate) fn get_zid(&self, psid: &u64) -> Option<&ZenohId> { self.mappings.get((*psid).try_into().unwrap()) } #[inline] - pub(crate) fn set_local_psid_mapping(&mut self, psid: ZInt, local_psid: ZInt) { + pub(crate) fn set_local_psid_mapping(&mut self, psid: u64, local_psid: u64) { self.local_mappings .insert(psid.try_into().unwrap(), local_psid); } #[inline] - pub(crate) fn get_local_psid(&self, psid: &ZInt) -> Option<&ZInt> { + pub(crate) fn get_local_psid(&self, psid: &u64) -> Option<&u64> { self.local_mappings.get((*psid).try_into().unwrap()) } } @@ -185,8 +190,7 @@ impl Network { } #[inline] - pub(crate) fn get_local_context(&self, context: Option, link_id: usize) -> usize { - let context = context.unwrap_or(0); + pub(crate) fn get_local_context(&self, context: u64, link_id: usize) -> usize { match self.get_link(link_id) { Some(link) => match link.get_local_psid(&context) { Some(psid) => (*psid).try_into().unwrap_or(0), @@ -211,7 +215,7 @@ impl Network { let idx = self.graph.add_node(node); for link in self.links.values_mut() { if let Some((psid, _)) = link.mappings.iter().find(|(_, p)| **p == zid) { - link.local_mappings.insert(psid, idx.index() as ZInt); + link.local_mappings.insert(psid, idx.index() as u64); } } idx @@ -260,19 +264,31 @@ impl Network { } } - fn make_msg(&self, idxs: Vec<(NodeIndex, Details)>) -> ZenohMessage { - let mut list = vec![]; + fn make_msg(&self, idxs: Vec<(NodeIndex, Details)>) -> Result { + let mut link_states = vec![]; for (idx, details) in idxs { - list.push(self.make_link_state(idx, details)); + link_states.push(self.make_link_state(idx, details)); } - ZenohMessage::make_link_state_list(list, None) + let codec = Zenoh080Routing::new(); + let mut buf = ZBuf::empty(); + codec.write(&mut buf.writer(), &LinkStateList { link_states })?; + Ok(NetworkBody::OAM(Oam { + id: OAM_LINKSTATE, + body: ZExtBody::ZBuf(buf), + ext_qos: oam::ext::QoSType::oam_default(), + ext_tstamp: None, + }) + .into()) } fn send_on_link(&self, idxs: Vec<(NodeIndex, Details)>, transport: &TransportUnicast) { - let msg = self.make_msg(idxs); - log::trace!("{} Send to {:?} {:?}", self.name, transport.get_zid(), msg); - if let Err(e) = transport.handle_message(msg) { - log::debug!("{} Error sending LinkStateList: {}", self.name, e); + if let Ok(msg) = self.make_msg(idxs) { + log::trace!("{} Send to {:?} {:?}", self.name, transport.get_zid(), msg); + if let Err(e) = transport.schedule(msg) { + log::debug!("{} Error sending LinkStateList: {}", self.name, e); + } + } else { + log::error!("Failed to encode Linkstate message"); } } @@ -280,14 +296,17 @@ impl Network { where P: FnMut(&Link) -> bool, { - let msg = self.make_msg(idxs); - for link in self.links.values() { - if parameters(link) { - log::trace!("{} Send to {} {:?}", self.name, link.zid, msg); - if let Err(e) = link.transport.handle_message(msg.clone()) { - log::debug!("{} Error sending LinkStateList: {}", self.name, e); + if let Ok(msg) = self.make_msg(idxs) { + for link in self.links.values() { + if parameters(link) { + log::trace!("{} Send to {} {:?}", self.name, link.zid, msg); + if let Err(e) = link.transport.schedule(msg.clone()) { + log::debug!("{} Error sending LinkStateList: {}", self.name, e); + } } } + } else { + log::error!("Failed to encode Linkstate message"); } } @@ -474,7 +493,8 @@ impl Network { if !self.autoconnect.is_empty() { // Connect discovered peers - if self.runtime.manager().get_transport(&zid).is_none() + if task::block_on(self.runtime.manager().get_transport_unicast(&zid)) + .is_none() && self.autoconnect.matches(whatami) { if let Some(locators) = locators { @@ -592,7 +612,8 @@ impl Network { for (_, idx, _) in &link_states { let node = &self.graph[*idx]; if let Some(whatami) = node.whatami { - if self.runtime.manager().get_transport(&node.zid).is_none() + if task::block_on(self.runtime.manager().get_transport_unicast(&node.zid)) + .is_none() && self.autoconnect.matches(whatami) { if let Some(locators) = &node.locators { diff --git a/zenoh/src/net/routing/pubsub.rs b/zenoh/src/net/routing/pubsub.rs index f5d1d312ab..f45b8029e3 100644 --- a/zenoh/src/net/routing/pubsub.rs +++ b/zenoh/src/net/routing/pubsub.rs @@ -13,7 +13,9 @@ // use super::face::FaceState; use super::network::Network; -use super::resource::{DataRoutes, Direction, PullCaches, Resource, Route, SessionContext}; +use super::resource::{ + DataRoutes, Direction, PullCaches, Resource, Route, RoutingContext, SessionContext, +}; use super::router::{RoutingExpr, Tables, TablesLock}; use petgraph::graph::NodeIndex; use std::borrow::Cow; @@ -21,15 +23,20 @@ use std::collections::{HashMap, HashSet}; use std::convert::TryFrom; use std::sync::RwLock; use std::sync::{Arc, RwLockReadGuard}; -use zenoh_buffers::ZBuf; use zenoh_core::zread; -use zenoh_protocol::core::key_expr::keyexpr; use zenoh_protocol::{ core::{ - key_expr::OwnedKeyExpr, Channel, CongestionControl, Priority, Reliability, SubInfo, - SubMode, WhatAmI, WireExpr, ZInt, ZenohId, + key_expr::{keyexpr, OwnedKeyExpr}, + Reliability, WhatAmI, WireExpr, ZenohId, + }, + network::{ + declare::{ + common::ext::WireExprType, ext, subscriber::ext::SubscriberInfo, Declare, DeclareBody, + DeclareSubscriber, Mode, UndeclareSubscriber, + }, + Push, }, - zenoh::{DataInfo, RoutingContext}, + zenoh::PushBody, }; use zenoh_sync::get_mut_unchecked; @@ -40,7 +47,7 @@ fn send_sourced_subscription_to_net_childs( childs: &[NodeIndex], res: &Arc, src_face: Option<&Arc>, - sub_info: &SubInfo, + sub_info: &SubscriberInfo, routing_context: Option, ) { for child in childs { @@ -52,9 +59,18 @@ fn send_sourced_subscription_to_net_childs( log::debug!("Send subscription {} on {}", res.expr(), someface); - someface - .primitives - .decl_subscriber(&key_expr, sub_info, routing_context); + someface.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { + node_id: routing_context.unwrap_or(0), + }, + body: DeclareBody::DeclareSubscriber(DeclareSubscriber { + id: 0, // TODO + wire_expr: key_expr, + ext_info: *sub_info, + }), + }); } } None => log::trace!("Unable to find face for zid {}", net.graph[*child].zid), @@ -68,7 +84,7 @@ fn propagate_simple_subscription_to( tables: &mut Tables, dst_face: &mut Arc, res: &Arc, - sub_info: &SubInfo, + sub_info: &SubscriberInfo, src_face: &mut Arc, full_peer_net: bool, ) { @@ -97,16 +113,23 @@ fn propagate_simple_subscription_to( { get_mut_unchecked(dst_face).local_subs.insert(res.clone()); let key_expr = Resource::decl_key(res, dst_face); - dst_face - .primitives - .decl_subscriber(&key_expr, sub_info, None); + dst_face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareSubscriber(DeclareSubscriber { + id: 0, // TODO + wire_expr: key_expr, + ext_info: *sub_info, + }), + }); } } fn propagate_simple_subscription( tables: &mut Tables, res: &Arc, - sub_info: &SubInfo, + sub_info: &SubscriberInfo, src_face: &mut Arc, ) { let full_peer_net = tables.full_net(WhatAmI::Peer); @@ -130,7 +153,7 @@ fn propagate_simple_subscription( fn propagate_sourced_subscription( tables: &Tables, res: &Arc, - sub_info: &SubInfo, + sub_info: &SubscriberInfo, src_face: Option<&Arc>, source: &ZenohId, net_type: WhatAmI, @@ -146,7 +169,7 @@ fn propagate_sourced_subscription( res, src_face, sub_info, - Some(RoutingContext::new(tree_sid.index() as ZInt)), + Some(tree_sid.index() as u16), ); } else { log::trace!( @@ -169,7 +192,7 @@ fn register_router_subscription( tables: &mut Tables, face: &mut Arc, res: &mut Arc, - sub_info: &SubInfo, + sub_info: &SubscriberInfo, router: ZenohId, ) { if !res.context().router_subs.contains(&router) { @@ -204,10 +227,13 @@ pub fn declare_router_subscription( rtables: RwLockReadGuard, face: &mut Arc, expr: &WireExpr, - sub_info: &SubInfo, + sub_info: &SubscriberInfo, router: ZenohId, ) { - match rtables.get_mapping(face, &expr.scope).cloned() { + match rtables + .get_mapping(face, &expr.scope, expr.mapping) + .cloned() + { Some(mut prefix) => { let res = Resource::get_resource(&prefix, &expr.suffix); let (mut res, mut wtables) = @@ -256,7 +282,7 @@ fn register_peer_subscription( tables: &mut Tables, face: &mut Arc, res: &mut Arc, - sub_info: &SubInfo, + sub_info: &SubscriberInfo, peer: ZenohId, ) { if !res.context().peer_subs.contains(&peer) { @@ -282,10 +308,13 @@ pub fn declare_peer_subscription( rtables: RwLockReadGuard, face: &mut Arc, expr: &WireExpr, - sub_info: &SubInfo, + sub_info: &SubscriberInfo, peer: ZenohId, ) { - match rtables.get_mapping(face, &expr.scope).cloned() { + match rtables + .get_mapping(face, &expr.scope, expr.mapping) + .cloned() + { Some(mut prefix) => { let res = Resource::get_resource(&prefix, &expr.suffix); let (mut res, mut wtables) = @@ -309,8 +338,8 @@ pub fn declare_peer_subscription( }; register_peer_subscription(&mut wtables, face, &mut res, sub_info, peer); if wtables.whatami == WhatAmI::Router { - let mut propa_sub_info = sub_info.clone(); - propa_sub_info.mode = SubMode::Push; + let mut propa_sub_info = *sub_info; + propa_sub_info.mode = Mode::Push; let zid = wtables.zid; register_router_subscription(&mut wtables, face, &mut res, &propa_sub_info, zid); } @@ -340,7 +369,7 @@ fn register_client_subscription( _tables: &mut Tables, face: &mut Arc, res: &mut Arc, - sub_info: &SubInfo, + sub_info: &SubscriberInfo, ) { // Register subscription { @@ -349,12 +378,12 @@ fn register_client_subscription( match res.session_ctxs.get_mut(&face.id) { Some(ctx) => match &ctx.subs { Some(info) => { - if SubMode::Pull == info.mode { - get_mut_unchecked(ctx).subs = Some(sub_info.clone()); + if Mode::Pull == info.mode { + get_mut_unchecked(ctx).subs = Some(*sub_info); } } None => { - get_mut_unchecked(ctx).subs = Some(sub_info.clone()); + get_mut_unchecked(ctx).subs = Some(*sub_info); } }, None => { @@ -364,7 +393,7 @@ fn register_client_subscription( face: face.clone(), local_expr_id: None, remote_expr_id: None, - subs: Some(sub_info.clone()), + subs: Some(*sub_info), qabl: None, last_values: HashMap::new(), }), @@ -380,10 +409,13 @@ pub fn declare_client_subscription( rtables: RwLockReadGuard, face: &mut Arc, expr: &WireExpr, - sub_info: &SubInfo, + sub_info: &SubscriberInfo, ) { log::debug!("Register client subscription"); - match rtables.get_mapping(face, &expr.scope).cloned() { + match rtables + .get_mapping(face, &expr.scope, expr.mapping) + .cloned() + { Some(mut prefix) => { let res = Resource::get_resource(&prefix, &expr.suffix); let (mut res, mut wtables) = @@ -407,8 +439,8 @@ pub fn declare_client_subscription( }; register_client_subscription(&mut wtables, face, &mut res, sub_info); - let mut propa_sub_info = sub_info.clone(); - propa_sub_info.mode = SubMode::Push; + let mut propa_sub_info = *sub_info; + propa_sub_info.mode = Mode::Push; match wtables.whatami { WhatAmI::Router => { let zid = wtables.zid; @@ -432,10 +464,40 @@ pub fn declare_client_subscription( ); } else { propagate_simple_subscription(&mut wtables, &res, &propa_sub_info, face); + // This introduced a buffer overflow on windows + // TODO: Let's deactivate this on windows until Fixed + #[cfg(not(windows))] + for mcast_group in &wtables.mcast_groups { + mcast_group.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareSubscriber(DeclareSubscriber { + id: 0, // TODO + wire_expr: res.expr().into(), + ext_info: *sub_info, + }), + }) + } } } _ => { propagate_simple_subscription(&mut wtables, &res, &propa_sub_info, face); + // This introduced a buffer overflow on windows + // TODO: Let's deactivate this on windows until Fixed + #[cfg(not(windows))] + for mcast_group in &wtables.mcast_groups { + mcast_group.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareSubscriber(DeclareSubscriber { + id: 0, // TODO + wire_expr: res.expr().into(), + ext_info: *sub_info, + }), + }) + } } } disable_matches_data_routes(&mut wtables, &mut res); @@ -505,13 +567,21 @@ fn send_forget_sourced_subscription_to_net_childs( match tables.get_face(&net.graph[*child].zid).cloned() { Some(mut someface) => { if src_face.is_none() || someface.id != src_face.unwrap().id { - let key_expr = Resource::decl_key(res, &mut someface); + let wire_expr = Resource::decl_key(res, &mut someface); log::debug!("Send forget subscription {} on {}", res.expr(), someface); - someface - .primitives - .forget_subscriber(&key_expr, routing_context); + someface.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { + node_id: routing_context.unwrap_or(0), + }, + body: DeclareBody::UndeclareSubscriber(UndeclareSubscriber { + id: 0, // TODO + ext_wire_expr: WireExprType { wire_expr }, + }), + }); } } None => log::trace!("Unable to find face for zid {}", net.graph[*child].zid), @@ -523,9 +593,16 @@ fn send_forget_sourced_subscription_to_net_childs( fn propagate_forget_simple_subscription(tables: &mut Tables, res: &Arc) { for face in tables.faces.values_mut() { if face.local_subs.contains(res) { - let key_expr = Resource::get_best_key(res, "", face.id); - face.primitives.forget_subscriber(&key_expr, None); - + let wire_expr = Resource::get_best_key(res, "", face.id); + face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::UndeclareSubscriber(UndeclareSubscriber { + id: 0, // TODO + ext_wire_expr: WireExprType { wire_expr }, + }), + }); get_mut_unchecked(face).local_subs.remove(res); } } @@ -552,8 +629,16 @@ fn propagate_forget_simple_subscription_to_peers(tables: &mut Tables, res: &Arc< && tables.failover_brokering(s.face.zid, face.zid))) }) { - let key_expr = Resource::get_best_key(res, "", face.id); - face.primitives.forget_subscriber(&key_expr, None); + let wire_expr = Resource::get_best_key(res, "", face.id); + face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::UndeclareSubscriber(UndeclareSubscriber { + id: 0, // TODO + ext_wire_expr: WireExprType { wire_expr }, + }), + }); get_mut_unchecked(&mut face).local_subs.remove(res); } @@ -578,7 +663,7 @@ fn propagate_forget_sourced_subscription( &net.trees[tree_sid.index()].childs, res, src_face, - Some(RoutingContext::new(tree_sid.index() as ZInt)), + Some(tree_sid.index() as u16), ); } else { log::trace!( @@ -639,7 +724,7 @@ pub fn forget_router_subscription( expr: &WireExpr, router: &ZenohId, ) { - match rtables.get_mapping(face, &expr.scope) { + match rtables.get_mapping(face, &expr.scope, expr.mapping) { Some(prefix) => match Resource::get_resource(prefix, expr.suffix.as_ref()) { Some(mut res) => { drop(rtables); @@ -705,7 +790,7 @@ pub fn forget_peer_subscription( expr: &WireExpr, peer: &ZenohId, ) { - match rtables.get_mapping(face, &expr.scope) { + match rtables.get_mapping(face, &expr.scope, expr.mapping) { Some(prefix) => match Resource::get_resource(prefix, expr.suffix.as_ref()) { Some(mut res) => { drop(rtables); @@ -783,8 +868,16 @@ pub(crate) fn undeclare_client_subscription( && !(face.whatami == WhatAmI::Client && res.expr().starts_with(super::PREFIX_LIVELINESS)) { - let key_expr = Resource::get_best_key(res, "", face.id); - face.primitives.forget_subscriber(&key_expr, None); + let wire_expr = Resource::get_best_key(res, "", face.id); + face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::UndeclareSubscriber(UndeclareSubscriber { + id: 0, // TODO + ext_wire_expr: WireExprType { wire_expr }, + }), + }); get_mut_unchecked(face).local_subs.remove(res); } @@ -797,7 +890,7 @@ pub fn forget_client_subscription( face: &mut Arc, expr: &WireExpr, ) { - match rtables.get_mapping(face, &expr.scope) { + match rtables.get_mapping(face, &expr.scope, expr.mapping) { Some(prefix) => match Resource::get_resource(prefix, expr.suffix.as_ref()) { Some(mut res) => { drop(rtables); @@ -826,9 +919,9 @@ pub fn forget_client_subscription( } pub(crate) fn pubsub_new_face(tables: &mut Tables, face: &mut Arc) { - let sub_info = SubInfo { + let sub_info = SubscriberInfo { reliability: Reliability::Reliable, // @TODO - mode: SubMode::Push, + mode: Mode::Push, }; match tables.whatami { WhatAmI::Router => { @@ -836,7 +929,16 @@ pub(crate) fn pubsub_new_face(tables: &mut Tables, face: &mut Arc) { for sub in &tables.router_subs { get_mut_unchecked(face).local_subs.insert(sub.clone()); let key_expr = Resource::decl_key(sub, face); - face.primitives.decl_subscriber(&key_expr, &sub_info, None); + face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareSubscriber(DeclareSubscriber { + id: 0, // TODO + wire_expr: key_expr, + ext_info: sub_info, + }), + }); } } else if face.whatami == WhatAmI::Peer && !tables.full_net(WhatAmI::Peer) { for sub in &tables.router_subs { @@ -851,7 +953,16 @@ pub(crate) fn pubsub_new_face(tables: &mut Tables, face: &mut Arc) { { get_mut_unchecked(face).local_subs.insert(sub.clone()); let key_expr = Resource::decl_key(sub, face); - face.primitives.decl_subscriber(&key_expr, &sub_info, None); + face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareSubscriber(DeclareSubscriber { + id: 0, // TODO + wire_expr: key_expr, + ext_info: sub_info, + }), + }); } } } @@ -862,7 +973,16 @@ pub(crate) fn pubsub_new_face(tables: &mut Tables, face: &mut Arc) { for sub in &tables.peer_subs { get_mut_unchecked(face).local_subs.insert(sub.clone()); let key_expr = Resource::decl_key(sub, face); - face.primitives.decl_subscriber(&key_expr, &sub_info, None); + face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareSubscriber(DeclareSubscriber { + id: 0, // TODO + wire_expr: key_expr, + ext_info: sub_info, + }), + }); } } } else { @@ -985,9 +1105,9 @@ pub(crate) fn pubsub_tree_change( }; for sub in subs { if *sub == tree_id { - let sub_info = SubInfo { + let sub_info = SubscriberInfo { reliability: Reliability::Reliable, // @TODO - mode: SubMode::Push, + mode: Mode::Push, }; send_sourced_subscription_to_net_childs( tables, @@ -996,7 +1116,7 @@ pub(crate) fn pubsub_tree_change( res, None, &sub_info, - Some(RoutingContext::new(tree_sid as ZInt)), + Some(tree_sid as u16), ); } } @@ -1045,8 +1165,18 @@ pub(crate) fn pubsub_linkstate_change(tables: &mut Tables, zid: &ZenohId, links: }) }; if forget { - let key_expr = Resource::get_best_key(res, "", dst_face.id); - dst_face.primitives.forget_subscriber(&key_expr, None); + let wire_expr = Resource::get_best_key(res, "", dst_face.id); + dst_face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::UndeclareSubscriber( + UndeclareSubscriber { + id: 0, // TODO + ext_wire_expr: WireExprType { wire_expr }, + }, + ), + }); get_mut_unchecked(dst_face).local_subs.remove(res); } @@ -1054,13 +1184,20 @@ pub(crate) fn pubsub_linkstate_change(tables: &mut Tables, zid: &ZenohId, links: let dst_face = &mut get_mut_unchecked(ctx).face; get_mut_unchecked(dst_face).local_subs.insert(res.clone()); let key_expr = Resource::decl_key(res, dst_face); - let sub_info = SubInfo { + let sub_info = SubscriberInfo { reliability: Reliability::Reliable, // TODO - mode: SubMode::Push, + mode: Mode::Push, }; - dst_face - .primitives - .decl_subscriber(&key_expr, &sub_info, None); + dst_face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareSubscriber(DeclareSubscriber { + id: 0, // TODO + wire_expr: key_expr, + ext_info: sub_info, + }), + }); } } } @@ -1093,7 +1230,7 @@ fn insert_faces_for_subs( face.clone(), key_expr.to_owned(), if source != 0 { - Some(RoutingContext::new(source as ZInt)) + Some(source as u16) } else { None }, @@ -1206,7 +1343,7 @@ fn compute_data_route( source_type == WhatAmI::Client || context.face.whatami == WhatAmI::Client } - } && subinfo.mode == SubMode::Push + } && subinfo.mode == Mode::Push { route.entry(*sid).or_insert_with(|| { let key_expr = Resource::get_best_key(expr.prefix, expr.suffix, *sid); @@ -1248,7 +1385,7 @@ fn compute_matching_pulls(tables: &Tables, expr: &mut RoutingExpr) -> Arc { + ($hlc:expr, $payload:expr, $drop:expr) => { // if an HLC was configured (via Config.add_timestamp), // check DataInfo and add a timestamp if there isn't - match $hlc { - Some(hlc) => { - if let Some(mut data_info) = $info { - if let Some(ref ts) = data_info.timestamp { - // Timestamp is present; update HLC with it (possibly raising error if delta exceed) - match hlc.update_with_timestamp(ts) { - Ok(()) => Some(data_info), - Err(e) => { - if $drop { - log::error!( - "Error treating timestamp for received Data ({}). Drop it!", - e - ); - return; - } else { - data_info.timestamp = Some(hlc.new_timestamp()); - log::error!( - "Error treating timestamp for received Data ({}). Replace timestamp: {:?}", - e, - data_info.timestamp); - Some(data_info) - } + if let Some(hlc) = $hlc { + if let PushBody::Put(data) = &mut $payload { + if let Some(ref ts) = data.timestamp { + // Timestamp is present; update HLC with it (possibly raising error if delta exceed) + match hlc.update_with_timestamp(ts) { + Ok(()) => (), + Err(e) => { + if $drop { + log::error!( + "Error treating timestamp for received Data ({}). Drop it!", + e + ); + return; + } else { + data.timestamp = Some(hlc.new_timestamp()); + log::error!( + "Error treating timestamp for received Data ({}). Replace timestamp: {:?}", + e, + data.timestamp); } } - } else { - // Timestamp not present; add one - data_info.timestamp = Some(hlc.new_timestamp()); - log::trace!("Adding timestamp to DataInfo: {:?}", data_info.timestamp); - Some(data_info) } } else { - // No DataInfo; add one with a Timestamp - let data_info = DataInfo { - timestamp: Some(hlc.new_timestamp()), - ..Default::default() - }; - Some(data_info) + // Timestamp not present; add one + data.timestamp = Some(hlc.new_timestamp()); + log::trace!("Adding timestamp to DataInfo: {:?}", data.timestamp); } - }, - None => $info, + } } } } @@ -1473,14 +1598,13 @@ fn get_data_route( face: &FaceState, res: &Option>, expr: &mut RoutingExpr, - routing_context: Option, + routing_context: u64, ) -> Arc { match tables.whatami { WhatAmI::Router => match face.whatami { WhatAmI::Router => { let routers_net = tables.routers_net.as_ref().unwrap(); - let local_context = routers_net - .get_local_context(routing_context.map(|rc| rc.tree_id), face.link_id); + let local_context = routers_net.get_local_context(routing_context, face.link_id); res.as_ref() .and_then(|res| res.routers_data_route(local_context)) .unwrap_or_else(|| { @@ -1490,8 +1614,7 @@ fn get_data_route( WhatAmI::Peer => { if tables.full_net(WhatAmI::Peer) { let peers_net = tables.peers_net.as_ref().unwrap(); - let local_context = peers_net - .get_local_context(routing_context.map(|rc| rc.tree_id), face.link_id); + let local_context = peers_net.get_local_context(routing_context, face.link_id); res.as_ref() .and_then(|res| res.peers_data_route(local_context)) .unwrap_or_else(|| { @@ -1513,8 +1636,8 @@ fn get_data_route( match face.whatami { WhatAmI::Router | WhatAmI::Peer => { let peers_net = tables.peers_net.as_ref().unwrap(); - let local_context = peers_net - .get_local_context(routing_context.map(|rc| rc.tree_id), face.link_id); + let local_context = + peers_net.get_local_context(routing_context, face.link_id); res.as_ref() .and_then(|res| res.peers_data_route(local_context)) .unwrap_or_else(|| { @@ -1558,14 +1681,12 @@ macro_rules! cache_data { ( $matching_pulls:expr, $expr:expr, - $payload:expr, - $info:expr + $payload:expr ) => { for context in $matching_pulls.iter() { - get_mut_unchecked(&mut context.clone()).last_values.insert( - $expr.full_expr().to_string(), - ($info.clone(), $payload.clone()), - ); + get_mut_unchecked(&mut context.clone()) + .last_values + .insert($expr.full_expr().to_string(), $payload.clone()); } }; } @@ -1598,19 +1719,40 @@ fn should_route( false } +#[cfg(feature = "stats")] +macro_rules! inc_stats { + ( + $face:expr, + $txrx:ident, + $space:ident, + $body:expr + ) => { + paste::paste! { + if let Some(stats) = $face.stats.as_ref() { + use zenoh_buffers::SplitBuffer; + match &$body { + PushBody::Put(p) => { + stats.[](1); + stats.[](p.payload.len()); + } + PushBody::Del(_) => stats.[](1), + } + } + } + }; +} + #[allow(clippy::too_many_arguments)] pub fn full_reentrant_route_data( tables_ref: &RwLock, face: &FaceState, expr: &WireExpr, - channel: Channel, - congestion_control: CongestionControl, - info: Option, - payload: ZBuf, - routing_context: Option, + ext_qos: ext::QoSType, + mut payload: PushBody, + routing_context: u64, ) { let tables = zread!(tables_ref); - match tables.get_mapping(face, &expr.scope).cloned() { + match tables.get_mapping(face, &expr.scope, expr.mapping).cloned() { Some(prefix) => { log::trace!( "Route data for res {}{}", @@ -1619,6 +1761,15 @@ pub fn full_reentrant_route_data( ); let mut expr = RoutingExpr::new(&prefix, expr.suffix.as_ref()); + #[cfg(feature = "stats")] + let admin = expr.full_expr().starts_with("@/"); + #[cfg(feature = "stats")] + if !admin { + inc_stats!(face, rx, user, payload) + } else { + inc_stats!(face, rx, admin, payload) + } + if tables.whatami != WhatAmI::Router || face.whatami != WhatAmI::Peer || tables.peers_net.is_none() @@ -1630,26 +1781,33 @@ pub fn full_reentrant_route_data( let matching_pulls = get_matching_pulls(&tables, &res, &mut expr); if !(route.is_empty() && matching_pulls.is_empty()) { - let data_info = - treat_timestamp!(&tables.hlc, info, tables.drop_future_timestamp); + treat_timestamp!(&tables.hlc, payload, tables.drop_future_timestamp); if route.len() == 1 && matching_pulls.len() == 0 { let (outface, key_expr, context) = route.values().next().unwrap(); if should_route(&tables, face, outface, &mut expr) { drop(tables); - outface.primitives.send_data( - key_expr, + #[cfg(feature = "stats")] + if !admin { + inc_stats!(face, tx, user, payload) + } else { + inc_stats!(face, tx, admin, payload) + } + + outface.primitives.send_push(Push { + wire_expr: key_expr.into(), + ext_qos, + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { + node_id: context.unwrap_or(0), + }, payload, - channel, // @TODO: Need to check the active subscriptions to determine the right reliability value - congestion_control, - data_info, - *context, - ) + }) } } else { if !matching_pulls.is_empty() { let lock = zlock!(tables.pull_caches_lock); - cache_data!(matching_pulls, expr, payload, data_info); + cache_data!(matching_pulls, expr, payload); drop(lock); } @@ -1664,14 +1822,22 @@ pub fn full_reentrant_route_data( drop(tables); for (outface, key_expr, context) in route { - outface.primitives.send_data( - &key_expr, - payload.clone(), - channel, // @TODO: Need to check the active subscriptions to determine the right reliability value - congestion_control, - data_info.clone(), - context, - ) + #[cfg(feature = "stats")] + if !admin { + inc_stats!(face, tx, user, payload) + } else { + inc_stats!(face, tx, admin, payload) + } + + outface.primitives.send_push(Push { + wire_expr: key_expr, + ext_qos, + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { + node_id: context.unwrap_or(0), + }, + payload: payload.clone(), + }) } } else { drop(tables); @@ -1685,14 +1851,22 @@ pub fn full_reentrant_route_data( _ => true, } { - outface.primitives.send_data( - key_expr, - payload.clone(), - channel, // @TODO: Need to check the active subscriptions to determine the right reliability value - congestion_control, - data_info.clone(), - *context, - ) + #[cfg(feature = "stats")] + if !admin { + inc_stats!(face, tx, user, payload) + } else { + inc_stats!(face, tx, admin, payload) + } + + outface.primitives.send_push(Push { + wire_expr: key_expr.into(), + ext_qos, + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { + node_id: context.unwrap_or(0), + }, + payload: payload.clone(), + }) } } } @@ -1706,23 +1880,16 @@ pub fn full_reentrant_route_data( } } -pub fn pull_data( - tables_ref: &RwLock, - face: &Arc, - _is_final: bool, - expr: &WireExpr, - _pull_id: ZInt, - _max_samples: &Option, -) { +pub fn pull_data(tables_ref: &RwLock, face: &Arc, expr: WireExpr) { let tables = zread!(tables_ref); - match tables.get_mapping(face, &expr.scope) { + match tables.get_mapping(face, &expr.scope, expr.mapping) { Some(prefix) => match Resource::get_resource(prefix, expr.suffix.as_ref()) { Some(mut res) => { let res = get_mut_unchecked(&mut res); match res.session_ctxs.get_mut(&face.id) { Some(ctx) => match &ctx.subs { - Some(subinfo) => { - let reliability = subinfo.reliability; + Some(_subinfo) => { + // let reliability = subinfo.reliability; let lock = zlock!(tables.pull_caches_lock); let route = get_mut_unchecked(ctx) .last_values @@ -1734,21 +1901,17 @@ pub fn pull_data( sample, ) }) - .collect::, ZBuf))>>(); + .collect::>(); drop(lock); drop(tables); - for (key_expr, (info, data)) in route { - face.primitives.send_data( - &key_expr, - data, - Channel { - priority: Priority::default(), // @TODO: Default value for the time being - reliability, - }, - CongestionControl::default(), // @TODO: Default value for the time being - info, - None, - ); + for (key_expr, payload) in route { + face.primitives.send_push(Push { + wire_expr: key_expr, + ext_qos: ext::QoSType::push_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + payload, + }); } } None => { diff --git a/zenoh/src/net/routing/queries.rs b/zenoh/src/net/routing/queries.rs index 2777e1ad26..e2608a0066 100644 --- a/zenoh/src/net/routing/queries.rs +++ b/zenoh/src/net/routing/queries.rs @@ -14,7 +14,8 @@ use super::face::FaceState; use super::network::Network; use super::resource::{ - QueryRoute, QueryRoutes, QueryTargetQabl, QueryTargetQablSet, Resource, SessionContext, + QueryRoute, QueryRoutes, QueryTargetQabl, QueryTargetQablSet, Resource, RoutingContext, + SessionContext, }; use super::router::{RoutingExpr, Tables, TablesLock}; use async_trait::async_trait; @@ -25,23 +26,30 @@ use std::collections::HashMap; use std::convert::TryFrom; use std::sync::{Arc, RwLockReadGuard, Weak}; use zenoh_buffers::ZBuf; -use zenoh_protocol::core::key_expr::keyexpr; use zenoh_protocol::{ core::{ key_expr::{ include::{Includer, DEFAULT_INCLUDER}, - OwnedKeyExpr, + keyexpr, OwnedKeyExpr, }, - ConsolidationMode, QueryTarget, QueryableInfo, WhatAmI, WireExpr, ZInt, ZenohId, + Encoding, WhatAmI, WireExpr, ZenohId, }, - zenoh::{DataInfo, QueryBody, RoutingContext}, + network::{ + declare::{ + common::ext::WireExprType, ext, queryable::ext::QueryableInfo, Declare, DeclareBody, + DeclareQueryable, UndeclareQueryable, + }, + request::{ext::TargetType, Request, RequestId}, + response::{self, ext::ResponderIdType, Response, ResponseFinal}, + }, + zenoh::{reply::ext::ConsolidationType, Reply, RequestBody, ResponseBody}, }; use zenoh_sync::get_mut_unchecked; use zenoh_util::Timed; pub(crate) struct Query { src_face: Arc, - src_qid: ZInt, + src_qid: RequestId, } #[cfg(feature = "complete_n")] @@ -55,7 +63,7 @@ fn merge_qabl_infos(mut this: QueryableInfo, info: &QueryableInfo) -> QueryableI #[cfg(not(feature = "complete_n"))] #[inline] fn merge_qabl_infos(mut this: QueryableInfo, info: &QueryableInfo) -> QueryableInfo { - this.complete = ZInt::from(this.complete != 0 || info.complete != 0); + this.complete = u8::from(this.complete != 0 || info.complete != 0); this.distance = std::cmp::min(this.distance, info.distance); this } @@ -67,7 +75,7 @@ fn local_router_qabl_info(tables: &Tables, res: &Arc) -> QueryableInfo if *zid != tables.zid { Some(match accu { Some(accu) => merge_qabl_infos(accu, info), - None => info.clone(), + None => *info, }) } else { accu @@ -83,7 +91,7 @@ fn local_router_qabl_info(tables: &Tables, res: &Arc) -> QueryableInfo if let Some(info) = ctx.qabl.as_ref() { Some(match accu { Some(accu) => merge_qabl_infos(accu, info), - None => info.clone(), + None => *info, }) } else { accu @@ -104,7 +112,7 @@ fn local_peer_qabl_info(tables: &Tables, res: &Arc) -> QueryableInfo { if *zid != tables.zid { Some(match accu { Some(accu) => merge_qabl_infos(accu, info), - None => info.clone(), + None => *info, }) } else { accu @@ -119,7 +127,7 @@ fn local_peer_qabl_info(tables: &Tables, res: &Arc) -> QueryableInfo { if let Some(info) = ctx.qabl.as_ref() { Some(match accu { Some(accu) => merge_qabl_infos(accu, info), - None => info.clone(), + None => *info, }) } else { accu @@ -140,7 +148,7 @@ fn local_qabl_info(tables: &Tables, res: &Arc, face: &Arc) if *zid != tables.zid { Some(match accu { Some(accu) => merge_qabl_infos(accu, info), - None => info.clone(), + None => *info, }) } else { accu @@ -158,7 +166,7 @@ fn local_qabl_info(tables: &Tables, res: &Arc, face: &Arc) if *zid != tables.zid { Some(match accu { Some(accu) => merge_qabl_infos(accu, info), - None => info.clone(), + None => *info, }) } else { accu @@ -175,7 +183,7 @@ fn local_qabl_info(tables: &Tables, res: &Arc, face: &Arc) if let Some(info) = ctx.qabl.as_ref() { Some(match accu { Some(accu) => merge_qabl_infos(accu, info), - None => info.clone(), + None => *info, }) } else { accu @@ -210,9 +218,18 @@ fn send_sourced_queryable_to_net_childs( log::debug!("Send queryable {} on {}", res.expr(), someface); - someface - .primitives - .decl_queryable(&key_expr, qabl_info, routing_context); + someface.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { + node_id: routing_context.unwrap_or(0), + }, + body: DeclareBody::DeclareQueryable(DeclareQueryable { + id: 0, // TODO + wire_expr: key_expr, + ext_info: *qabl_info, + }), + }); } } None => log::trace!("Unable to find face for zid {}", net.graph[*child].zid), @@ -266,9 +283,18 @@ fn propagate_simple_queryable( { get_mut_unchecked(&mut dst_face) .local_qabls - .insert(res.clone(), info.clone()); + .insert(res.clone(), info); let key_expr = Resource::decl_key(res, &mut dst_face); - dst_face.primitives.decl_queryable(&key_expr, &info, None); + dst_face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareQueryable(DeclareQueryable { + id: 0, // TODO + wire_expr: key_expr, + ext_info: info, + }), + }); } } } @@ -292,7 +318,7 @@ fn propagate_sourced_queryable( res, qabl_info, src_face, - Some(RoutingContext::new(tree_sid.index() as ZInt)), + Some(tree_sid.index() as u16), ); } else { log::trace!( @@ -330,7 +356,7 @@ fn register_router_queryable( get_mut_unchecked(res) .context_mut() .router_qabls - .insert(router, qabl_info.clone()); + .insert(router, *qabl_info); tables.router_qabls.insert(res.clone()); } @@ -365,7 +391,10 @@ pub fn declare_router_queryable( qabl_info: &QueryableInfo, router: ZenohId, ) { - match rtables.get_mapping(face, &expr.scope).cloned() { + match rtables + .get_mapping(face, &expr.scope, expr.mapping) + .cloned() + { Some(mut prefix) => { let res = Resource::get_resource(&prefix, &expr.suffix); let (mut res, mut wtables) = @@ -376,6 +405,7 @@ pub fn declare_router_queryable( } else { let mut fullexpr = prefix.expr(); fullexpr.push_str(expr.suffix.as_ref()); + log::debug!("Register router queryable {}", fullexpr); let mut matches = keyexpr::new(fullexpr.as_str()) .map(|ke| Resource::get_matches(&rtables, ke)) .unwrap_or_default(); @@ -422,7 +452,7 @@ fn register_peer_queryable( get_mut_unchecked(res) .context_mut() .peer_qabls - .insert(peer, qabl_info.clone()); + .insert(peer, *qabl_info); tables.peer_qabls.insert(res.clone()); } @@ -451,7 +481,10 @@ pub fn declare_peer_queryable( qabl_info: &QueryableInfo, peer: ZenohId, ) { - match rtables.get_mapping(face, &expr.scope).cloned() { + match rtables + .get_mapping(face, &expr.scope, expr.mapping) + .cloned() + { Some(mut prefix) => { let res = Resource::get_resource(&prefix, &expr.suffix); let (mut res, mut wtables) = @@ -462,6 +495,7 @@ pub fn declare_peer_queryable( } else { let mut fullexpr = prefix.expr(); fullexpr.push_str(expr.suffix.as_ref()); + log::debug!("Register peer queryable {}", fullexpr); let mut matches = keyexpr::new(fullexpr.as_str()) .map(|ke| Resource::get_matches(&rtables, ke)) .unwrap_or_default(); @@ -519,7 +553,7 @@ fn register_client_queryable( last_values: HashMap::new(), }) })) - .qabl = Some(qabl_info.clone()); + .qabl = Some(*qabl_info); } get_mut_unchecked(face).remote_qabls.insert(res.clone()); } @@ -531,7 +565,10 @@ pub fn declare_client_queryable( expr: &WireExpr, qabl_info: &QueryableInfo, ) { - match rtables.get_mapping(face, &expr.scope).cloned() { + match rtables + .get_mapping(face, &expr.scope, expr.mapping) + .cloned() + { Some(mut prefix) => { let res = Resource::get_resource(&prefix, &expr.suffix); let (mut res, mut wtables) = @@ -542,6 +579,7 @@ pub fn declare_client_queryable( } else { let mut fullexpr = prefix.expr(); fullexpr.push_str(expr.suffix.as_ref()); + log::debug!("Register client queryable {}", fullexpr); let mut matches = keyexpr::new(fullexpr.as_str()) .map(|ke| Resource::get_matches(&rtables, ke)) .unwrap_or_default(); @@ -654,13 +692,21 @@ fn send_forget_sourced_queryable_to_net_childs( match tables.get_face(&net.graph[*child].zid).cloned() { Some(mut someface) => { if src_face.is_none() || someface.id != src_face.unwrap().id { - let key_expr = Resource::decl_key(res, &mut someface); + let wire_expr = Resource::decl_key(res, &mut someface); log::debug!("Send forget queryable {} on {}", res.expr(), someface); - someface - .primitives - .forget_queryable(&key_expr, routing_context); + someface.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { + node_id: routing_context.unwrap_or(0), + }, + body: DeclareBody::UndeclareQueryable(UndeclareQueryable { + id: 0, // TODO + ext_wire_expr: WireExprType { wire_expr }, + }), + }); } } None => log::trace!("Unable to find face for zid {}", net.graph[*child].zid), @@ -672,8 +718,16 @@ fn send_forget_sourced_queryable_to_net_childs( fn propagate_forget_simple_queryable(tables: &mut Tables, res: &mut Arc) { for face in tables.faces.values_mut() { if face.local_qabls.contains_key(res) { - let key_expr = Resource::get_best_key(res, "", face.id); - face.primitives.forget_queryable(&key_expr, None); + let wire_expr = Resource::get_best_key(res, "", face.id); + face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::UndeclareQueryable(UndeclareQueryable { + id: 0, // TODO + ext_wire_expr: WireExprType { wire_expr }, + }), + }); get_mut_unchecked(face).local_qabls.remove(res); } @@ -701,8 +755,16 @@ fn propagate_forget_simple_queryable_to_peers(tables: &mut Tables, res: &mut Arc && tables.failover_brokering(s.face.zid, face.zid))) }) { - let key_expr = Resource::get_best_key(res, "", face.id); - face.primitives.forget_queryable(&key_expr, None); + let wire_expr = Resource::get_best_key(res, "", face.id); + face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::UndeclareQueryable(UndeclareQueryable { + id: 0, // TODO + ext_wire_expr: WireExprType { wire_expr }, + }), + }); get_mut_unchecked(&mut face).local_qabls.remove(res); } @@ -727,7 +789,7 @@ fn propagate_forget_sourced_queryable( &net.trees[tree_sid.index()].childs, res, src_face, - Some(RoutingContext::new(tree_sid.index() as ZInt)), + Some(tree_sid.index() as u16), ); } else { log::trace!( @@ -788,7 +850,7 @@ pub fn forget_router_queryable( expr: &WireExpr, router: &ZenohId, ) { - match rtables.get_mapping(face, &expr.scope) { + match rtables.get_mapping(face, &expr.scope, expr.mapping) { Some(prefix) => match Resource::get_resource(prefix, expr.suffix.as_ref()) { Some(mut res) => { drop(rtables); @@ -848,7 +910,7 @@ pub fn forget_peer_queryable( expr: &WireExpr, peer: &ZenohId, ) { - match rtables.get_mapping(face, &expr.scope) { + match rtables.get_mapping(face, &expr.scope, expr.mapping) { Some(prefix) => match Resource::get_resource(prefix, expr.suffix.as_ref()) { Some(mut res) => { drop(rtables); @@ -940,8 +1002,16 @@ pub(crate) fn undeclare_client_queryable( if client_qabls.len() == 1 && !router_qabls && !peer_qabls { let face = &mut client_qabls[0]; if face.local_qabls.contains_key(res) { - let key_expr = Resource::get_best_key(res, "", face.id); - face.primitives.forget_queryable(&key_expr, None); + let wire_expr = Resource::get_best_key(res, "", face.id); + face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::UndeclareQueryable(UndeclareQueryable { + id: 0, // TODO + ext_wire_expr: WireExprType { wire_expr }, + }), + }); get_mut_unchecked(face).local_qabls.remove(res); } @@ -954,7 +1024,7 @@ pub fn forget_client_queryable( face: &mut Arc, expr: &WireExpr, ) { - match rtables.get_mapping(face, &expr.scope) { + match rtables.get_mapping(face, &expr.scope, expr.mapping) { Some(prefix) => match Resource::get_resource(prefix, expr.suffix.as_ref()) { Some(mut res) => { drop(rtables); @@ -991,9 +1061,18 @@ pub(crate) fn queries_new_face(tables: &mut Tables, face: &mut Arc) { let info = local_qabl_info(tables, qabl, face); get_mut_unchecked(face) .local_qabls - .insert(qabl.clone(), info.clone()); + .insert(qabl.clone(), info); let key_expr = Resource::decl_key(qabl, face); - face.primitives.decl_queryable(&key_expr, &info, None); + face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareQueryable(DeclareQueryable { + id: 0, // TODO + wire_expr: key_expr, + ext_info: info, + }), + }); } } } else if face.whatami == WhatAmI::Peer && !tables.full_net(WhatAmI::Peer) { @@ -1010,9 +1089,18 @@ pub(crate) fn queries_new_face(tables: &mut Tables, face: &mut Arc) { let info = local_qabl_info(tables, qabl, face); get_mut_unchecked(face) .local_qabls - .insert(qabl.clone(), info.clone()); + .insert(qabl.clone(), info); let key_expr = Resource::decl_key(qabl, face); - face.primitives.decl_queryable(&key_expr, &info, None); + face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareQueryable(DeclareQueryable { + id: 0, // TODO + wire_expr: key_expr, + ext_info: info, + }), + }); } } } @@ -1025,9 +1113,18 @@ pub(crate) fn queries_new_face(tables: &mut Tables, face: &mut Arc) { let info = local_qabl_info(tables, qabl, face); get_mut_unchecked(face) .local_qabls - .insert(qabl.clone(), info.clone()); + .insert(qabl.clone(), info); let key_expr = Resource::decl_key(qabl, face); - face.primitives.decl_queryable(&key_expr, &info, None); + face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareQueryable(DeclareQueryable { + id: 0, // TODO + wire_expr: key_expr, + ext_info: info, + }), + }); } } } @@ -1154,8 +1251,16 @@ pub(crate) fn queries_linkstate_change(tables: &mut Tables, zid: &ZenohId, links }) }; if forget { - let key_expr = Resource::get_best_key(res, "", dst_face.id); - dst_face.primitives.forget_queryable(&key_expr, None); + let wire_expr = Resource::get_best_key(res, "", dst_face.id); + dst_face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::UndeclareQueryable(UndeclareQueryable { + id: 0, // TODO + ext_wire_expr: WireExprType { wire_expr }, + }), + }); get_mut_unchecked(dst_face).local_qabls.remove(res); } @@ -1164,9 +1269,18 @@ pub(crate) fn queries_linkstate_change(tables: &mut Tables, zid: &ZenohId, links let info = local_qabl_info(tables, res, dst_face); get_mut_unchecked(dst_face) .local_qabls - .insert(res.clone(), info.clone()); + .insert(res.clone(), info); let key_expr = Resource::decl_key(res, dst_face); - dst_face.primitives.decl_queryable(&key_expr, &info, None); + dst_face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareQueryable(DeclareQueryable { + id: 0, // TODO + wire_expr: key_expr, + ext_info: info, + }), + }); } } } @@ -1207,7 +1321,7 @@ pub(crate) fn queries_tree_change( res, qabl_info, None, - Some(RoutingContext::new(tree_sid as ZInt)), + Some(tree_sid as u16), ); } } @@ -1245,12 +1359,16 @@ fn insert_target_for_qabls( face.clone(), key_expr.to_owned(), if source != 0 { - Some(RoutingContext::new(source as ZInt)) + Some(source as u16) } else { None }, ), - complete: if complete { qabl_info.complete } else { 0 }, + complete: if complete { + qabl_info.complete as u64 + } else { + 0 + }, distance: net.distances[qabl_idx.index()], }); } @@ -1369,7 +1487,11 @@ fn compute_query_route( if let Some(qabl_info) = context.qabl.as_ref() { route.push(QueryTargetQabl { direction: (context.face.clone(), key_expr.to_owned(), None), - complete: if complete { qabl_info.complete } else { 0 }, + complete: if complete { + qabl_info.complete as u64 + } else { + 0 + }, distance: 0.5, }); } @@ -1545,7 +1667,7 @@ pub(super) fn compute_matches_query_routes_( } #[inline] -fn insert_pending_query(outface: &mut Arc, query: Arc) -> ZInt { +fn insert_pending_query(outface: &mut Arc, query: Arc) -> RequestId { let outface_mut = get_mut_unchecked(outface); outface_mut.next_qid += 1; let qid = outface_mut.next_qid; @@ -1582,11 +1704,11 @@ fn compute_final_route( qabls: &Arc, src_face: &Arc, expr: &mut RoutingExpr, - target: &QueryTarget, + target: &TargetType, query: Arc, ) -> QueryRoute { match target { - QueryTarget::All => { + TargetType::All => { let mut route = HashMap::new(); for qabl in qabls.iter() { if should_route(tables, src_face, &qabl.direction.0, expr) { @@ -1610,7 +1732,7 @@ fn compute_final_route( } route } - QueryTarget::AllComplete => { + TargetType::AllComplete => { let mut route = HashMap::new(); for qabl in qabls.iter() { if qabl.complete > 0 && should_route(tables, src_face, &qabl.direction.0, expr) { @@ -1635,7 +1757,7 @@ fn compute_final_route( route } #[cfg(feature = "complete_n")] - QueryTarget::Complete(n) => { + TargetType::Complete(n) => { let mut route = HashMap::new(); let mut remaining = *n; if src_face.whatami == WhatAmI::Peer && !tables.full_net(WhatAmI::Peer) { @@ -1658,7 +1780,7 @@ fn compute_final_route( route.entry(qabl.direction.0.id).or_insert_with(|| { let mut direction = qabl.direction.clone(); let qid = insert_pending_query(&mut direction.0, query.clone()); - (direction, qid, QueryTarget::Complete(nb)) + (direction, qid, TargetType::Complete(nb)) }); remaining -= nb; if remaining == 0 { @@ -1673,7 +1795,7 @@ fn compute_final_route( route.entry(qabl.direction.0.id).or_insert_with(|| { let mut direction = qabl.direction.clone(); let qid = insert_pending_query(&mut direction.0, query.clone()); - (direction, qid, QueryTarget::Complete(nb)) + (direction, qid, TargetType::Complete(nb)) }); remaining -= nb; if remaining == 0 { @@ -1684,7 +1806,7 @@ fn compute_final_route( } route } - QueryTarget::BestMatching => { + TargetType::BestMatching => { if let Some(qabl) = qabls .iter() .find(|qabl| qabl.direction.0.id != src_face.id && qabl.complete > 0) @@ -1704,7 +1826,7 @@ fn compute_final_route( } route } else { - compute_final_route(tables, qabls, src_face, expr, &QueryTarget::All, query) + compute_final_route(tables, qabls, src_face, expr, &TargetType::All, query) } } } @@ -1755,7 +1877,7 @@ fn compute_local_replies( struct QueryCleanup { tables: Arc, face: Weak, - qid: ZInt, + qid: RequestId, } #[async_trait] @@ -1800,14 +1922,13 @@ fn get_query_route( face: &FaceState, res: &Option>, expr: &mut RoutingExpr, - routing_context: Option, + routing_context: u64, ) -> Arc { match tables.whatami { WhatAmI::Router => match face.whatami { WhatAmI::Router => { let routers_net = tables.routers_net.as_ref().unwrap(); - let local_context = routers_net - .get_local_context(routing_context.map(|rc| rc.tree_id), face.link_id); + let local_context = routers_net.get_local_context(routing_context, face.link_id); res.as_ref() .and_then(|res| res.routers_query_route(local_context)) .unwrap_or_else(|| { @@ -1817,8 +1938,7 @@ fn get_query_route( WhatAmI::Peer => { if tables.full_net(WhatAmI::Peer) { let peers_net = tables.peers_net.as_ref().unwrap(); - let local_context = peers_net - .get_local_context(routing_context.map(|rc| rc.tree_id), face.link_id); + let local_context = peers_net.get_local_context(routing_context, face.link_id); res.as_ref() .and_then(|res| res.peers_query_route(local_context)) .unwrap_or_else(|| { @@ -1840,8 +1960,8 @@ fn get_query_route( match face.whatami { WhatAmI::Router | WhatAmI::Peer => { let peers_net = tables.peers_net.as_ref().unwrap(); - let local_context = peers_net - .get_local_context(routing_context.map(|rc| rc.tree_id), face.link_id); + let local_context = + peers_net.get_local_context(routing_context, face.link_id); res.as_ref() .and_then(|res| res.peers_query_route(local_context)) .unwrap_or_else(|| { @@ -1869,20 +1989,81 @@ fn get_query_route( } } +#[cfg(feature = "stats")] +macro_rules! inc_req_stats { + ( + $face:expr, + $txrx:ident, + $space:ident, + $body:expr + ) => { + paste::paste! { + if let Some(stats) = $face.stats.as_ref() { + use zenoh_buffers::SplitBuffer; + match &$body { + RequestBody::Put(p) => { + stats.[](1); + stats.[](p.payload.len()); + } + RequestBody::Del(_) => stats.[](1), + RequestBody::Query(q) => { + stats.[](1); + stats.[]( + q.ext_body.as_ref().map(|b| b.payload.len()).unwrap_or(0), + ); + } + RequestBody::Pull(_) => (), + } + } + } + }; +} + +#[cfg(feature = "stats")] +macro_rules! inc_res_stats { + ( + $face:expr, + $txrx:ident, + $space:ident, + $body:expr + ) => { + paste::paste! { + if let Some(stats) = $face.stats.as_ref() { + use zenoh_buffers::SplitBuffer; + match &$body { + ResponseBody::Put(p) => { + stats.[](1); + stats.[](p.payload.len()); + } + ResponseBody::Reply(r) => { + stats.[](1); + stats.[](r.payload.len()); + } + ResponseBody::Err(e) => { + stats.[](1); + stats.[]( + e.ext_body.as_ref().map(|b| b.payload.len()).unwrap_or(0), + ); + } + ResponseBody::Ack(_) => (), + } + } + } + }; +} + #[allow(clippy::too_many_arguments)] pub fn route_query( tables_ref: &Arc, face: &Arc, expr: &WireExpr, - parameters: &str, - qid: ZInt, - target: QueryTarget, - consolidation: ConsolidationMode, - body: Option, - routing_context: Option, + qid: RequestId, + target: TargetType, + body: RequestBody, + routing_context: u64, ) { let rtables = zread!(tables_ref.tables); - match rtables.get_mapping(face, &expr.scope) { + match rtables.get_mapping(face, &expr.scope, expr.mapping) { Some(prefix) => { log::debug!( "Route query {}:{} for res {}{}", @@ -1894,6 +2075,15 @@ pub fn route_query( let prefix = prefix.clone(); let mut expr = RoutingExpr::new(&prefix, expr.suffix.as_ref()); + #[cfg(feature = "stats")] + let admin = expr.full_expr().starts_with("@/"); + #[cfg(feature = "stats")] + if !admin { + inc_req_stats!(face, rx, user, body) + } else { + inc_req_stats!(face, rx, admin, body) + } + if rtables.whatami != WhatAmI::Router || face.whatami != WhatAmI::Peer || rtables.peers_net.is_none() @@ -1917,9 +2107,34 @@ pub fn route_query( drop(rtables); for (expr, payload) in local_replies { - face.primitives - .clone() - .send_reply_data(qid, zid, expr, None, payload); + let payload = ResponseBody::Reply(Reply { + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + ext_consolidation: ConsolidationType::default(), + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + payload, + }); + #[cfg(feature = "stats")] + if !admin { + inc_res_stats!(face, tx, user, payload) + } else { + inc_res_stats!(face, tx, admin, payload) + } + + face.primitives.clone().send_response(Response { + rid: qid, + wire_expr: expr, + payload, + ext_qos: response::ext::QoSType::declare_default(), + ext_tstamp: None, + ext_respid: Some(response::ext::ResponderIdType { + zid, + eid: 0, // TODO + }), + }); } if route.is_empty() { @@ -1928,7 +2143,11 @@ pub fn route_query( face, qid ); - face.primitives.clone().send_reply_final(qid) + face.primitives.clone().send_response_final(ResponseFinal { + rid: qid, + ext_qos: response::ext::QoSType::response_final_default(), + ext_tstamp: None, + }); } else { // let timer = tables.timer.clone(); // let timeout = tables.queries_default_timeout; @@ -1943,16 +2162,27 @@ pub fn route_query( // *qid, // }, // )); + #[cfg(feature = "stats")] + if !admin { + inc_req_stats!(outface, tx, user, body) + } else { + inc_req_stats!(outface, tx, admin, body) + } + log::trace!("Propagate query {}:{} to {}", face, qid, outface); - outface.primitives.send_query( - key_expr, - parameters, - *qid, - *t, - consolidation, - body.clone(), - *context, - ); + outface.primitives.send_request(Request { + id: *qid, + wire_expr: key_expr.into(), + ext_qos: ext::QoSType::request_default(), // TODO + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { + node_id: context.map(|c| c.tree_id).unwrap_or(0) as u16, + }, + ext_target: *t, + ext_budget: None, + ext_timeout: None, + payload: body.clone(), + }); } } @@ -1967,23 +2197,38 @@ pub fn route_query( // *qid, // }, // )); + #[cfg(feature = "stats")] + if !admin { + inc_req_stats!(outface, tx, user, body) + } else { + inc_req_stats!(outface, tx, admin, body) + } + log::trace!("Propagate query {}:{} to {}", face, qid, outface); - outface.primitives.send_query( - key_expr, - parameters, - *qid, - target, - consolidation, - body.clone(), - *context, - ); + outface.primitives.send_request(Request { + id: *qid, + wire_expr: key_expr.into(), + ext_qos: ext::QoSType::request_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { + node_id: context.unwrap_or(0), + }, + ext_target: target, + ext_budget: None, + ext_timeout: None, + payload: body.clone(), + }); } } } } else { log::debug!("Send final reply {}:{} (not master)", face, qid); drop(rtables); - face.primitives.clone().send_reply_final(qid) + face.primitives.clone().send_response_final(ResponseFinal { + rid: qid, + ext_qos: response::ext::QoSType::response_final_default(), + ext_tstamp: None, + }); } } None => { @@ -1992,32 +2237,53 @@ pub fn route_query( expr.scope ); drop(rtables); - face.primitives.clone().send_reply_final(qid) + face.primitives.clone().send_response_final(ResponseFinal { + rid: qid, + ext_qos: response::ext::QoSType::response_final_default(), + ext_tstamp: None, + }); } } } #[allow(clippy::too_many_arguments)] -pub(crate) fn route_send_reply_data( +pub(crate) fn route_send_response( tables_ref: &Arc, face: &mut Arc, - qid: ZInt, - replier_id: ZenohId, + qid: RequestId, + ext_respid: Option, key_expr: WireExpr, - info: Option, - payload: ZBuf, + body: ResponseBody, ) { let queries_lock = zread!(tables_ref.queries_lock); + #[cfg(feature = "stats")] + let admin = key_expr.as_str().starts_with("@/"); + #[cfg(feature = "stats")] + if !admin { + inc_res_stats!(face, rx, user, body) + } else { + inc_res_stats!(face, rx, admin, body) + } + match face.pending_queries.get(&qid) { Some(query) => { drop(queries_lock); - query.src_face.primitives.clone().send_reply_data( - query.src_qid, - replier_id, - key_expr, - info, - payload, - ); + + #[cfg(feature = "stats")] + if !admin { + inc_res_stats!(query.src_face, tx, user, body) + } else { + inc_res_stats!(query.src_face, tx, admin, body) + } + + query.src_face.primitives.clone().send_response(Response { + rid: query.src_qid, + wire_expr: key_expr.to_owned(), + payload: body, + ext_qos: response::ext::QoSType::response_default(), + ext_tstamp: None, + ext_respid, + }); } None => log::warn!( "Route reply {}:{} from {}: Query nof found!", @@ -2028,10 +2294,10 @@ pub(crate) fn route_send_reply_data( } } -pub(crate) fn route_send_reply_final( +pub(crate) fn route_send_response_final( tables_ref: &Arc, face: &mut Arc, - qid: ZInt, + qid: RequestId, ) { let queries_lock = zwrite!(tables_ref.queries_lock); match get_mut_unchecked(face).pending_queries.remove(&qid) { @@ -2069,6 +2335,10 @@ pub(crate) fn finalize_pending_query(query: Arc) { .src_face .primitives .clone() - .send_reply_final(query.src_qid); + .send_response_final(ResponseFinal { + rid: query.src_qid, + ext_qos: response::ext::QoSType::response_final_default(), + ext_tstamp: None, + }); } } diff --git a/zenoh/src/net/routing/resource.rs b/zenoh/src/net/routing/resource.rs index 79a845b0a6..e26a9217f3 100644 --- a/zenoh/src/net/routing/resource.rs +++ b/zenoh/src/net/routing/resource.rs @@ -17,22 +17,33 @@ use std::collections::{HashMap, HashSet}; use std::convert::TryInto; use std::hash::{Hash, Hasher}; use std::sync::{Arc, Weak}; -use zenoh_buffers::ZBuf; +#[cfg(feature = "complete_n")] +use zenoh_protocol::network::request::ext::TargetType; +use zenoh_protocol::network::RequestId; +use zenoh_protocol::zenoh::PushBody; use zenoh_protocol::{ - core::{key_expr::keyexpr, QueryableInfo, SubInfo, WireExpr, ZInt, ZenohId}, - zenoh::{DataInfo, RoutingContext}, + core::{key_expr::keyexpr, ExprId, WireExpr, ZenohId}, + network::{ + declare::{ + ext, queryable::ext::QueryableInfo, subscriber::ext::SubscriberInfo, Declare, + DeclareBody, DeclareKeyExpr, + }, + Mapping, + }, }; use zenoh_sync::get_mut_unchecked; +pub(super) type RoutingContext = u16; + pub(super) type Direction = (Arc, WireExpr<'static>, Option); pub(super) type Route = HashMap; #[cfg(feature = "complete_n")] -pub(super) type QueryRoute = HashMap; +pub(super) type QueryRoute = HashMap; #[cfg(not(feature = "complete_n"))] -pub(super) type QueryRoute = HashMap; +pub(super) type QueryRoute = HashMap; pub(super) struct QueryTargetQabl { pub(super) direction: Direction, - pub(super) complete: ZInt, + pub(super) complete: u64, pub(super) distance: f64, } pub(super) type QueryTargetQablSet = Vec; @@ -40,11 +51,11 @@ pub(super) type PullCaches = Vec>; pub(super) struct SessionContext { pub(super) face: Arc, - pub(super) local_expr_id: Option, - pub(super) remote_expr_id: Option, - pub(super) subs: Option, + pub(super) local_expr_id: Option, + pub(super) remote_expr_id: Option, + pub(super) subs: Option, pub(super) qabl: Option, - pub(super) last_values: HashMap, ZBuf)>, + pub(super) last_values: HashMap, } pub(super) struct DataRoutes { @@ -486,22 +497,38 @@ impl Resource { }) }); - let expr_id = match ctx.local_expr_id.or(ctx.remote_expr_id) { - Some(expr_id) => expr_id, - None => { - let expr_id = face.get_next_local_id(); - get_mut_unchecked(ctx).local_expr_id = Some(expr_id); - get_mut_unchecked(face) - .local_mappings - .insert(expr_id, nonwild_prefix.clone()); - face.primitives - .decl_resource(expr_id, &nonwild_prefix.expr().into()); - expr_id + if let Some(expr_id) = ctx.remote_expr_id { + WireExpr { + scope: expr_id, + suffix: wildsuffix.into(), + mapping: Mapping::Receiver, + } + } else if let Some(expr_id) = ctx.local_expr_id { + WireExpr { + scope: expr_id, + suffix: wildsuffix.into(), + mapping: Mapping::Sender, + } + } else { + let expr_id = face.get_next_local_id(); + get_mut_unchecked(ctx).local_expr_id = Some(expr_id); + get_mut_unchecked(face) + .local_mappings + .insert(expr_id, nonwild_prefix.clone()); + face.primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareKeyExpr(DeclareKeyExpr { + id: expr_id, + wire_expr: nonwild_prefix.expr().into(), + }), + }); + WireExpr { + scope: expr_id, + suffix: wildsuffix.into(), + mapping: Mapping::Sender, } - }; - WireExpr { - scope: expr_id, - suffix: wildsuffix.into(), } } None => wildsuffix.into(), @@ -523,15 +550,17 @@ impl Resource { } } if let Some(ctx) = prefix.session_ctxs.get(&sid) { - if let Some(expr_id) = ctx.local_expr_id { + if let Some(expr_id) = ctx.remote_expr_id { return WireExpr { scope: expr_id, suffix: suffix.into(), + mapping: Mapping::Receiver, }; - } else if let Some(expr_id) = ctx.remote_expr_id { + } else if let Some(expr_id) = ctx.local_expr_id { return WireExpr { scope: expr_id, suffix: suffix.into(), + mapping: Mapping::Sender, }; } } @@ -655,11 +684,14 @@ impl Resource { pub fn register_expr( tables: &TablesLock, face: &mut Arc, - expr_id: ZInt, + expr_id: ExprId, expr: &WireExpr, ) { let rtables = zread!(tables.tables); - match rtables.get_mapping(face, &expr.scope).cloned() { + match rtables + .get_mapping(face, &expr.scope, expr.mapping) + .cloned() + { Some(mut prefix) => match face.remote_mappings.get(&expr_id) { Some(res) => { let mut fullexpr = prefix.expr(); @@ -692,7 +724,7 @@ pub fn register_expr( Resource::match_resource(&wtables, &mut res, matches); (res, wtables) }; - let mut ctx = get_mut_unchecked(&mut res) + get_mut_unchecked(&mut res) .session_ctxs .entry(face.id) .or_insert_with(|| { @@ -704,20 +736,7 @@ pub fn register_expr( qabl: None, last_values: HashMap::new(), }) - }) - .clone(); - - if face.local_mappings.get(&expr_id).is_some() && ctx.local_expr_id.is_none() { - let local_expr_id = get_mut_unchecked(face).get_next_local_id(); - get_mut_unchecked(&mut ctx).local_expr_id = Some(local_expr_id); - - get_mut_unchecked(face) - .local_mappings - .insert(local_expr_id, res.clone()); - - face.primitives - .decl_resource(local_expr_id, &res.expr().into()); - } + }); get_mut_unchecked(face) .remote_mappings @@ -730,7 +749,7 @@ pub fn register_expr( } } -pub fn unregister_expr(tables: &TablesLock, face: &mut Arc, expr_id: ZInt) { +pub fn unregister_expr(tables: &TablesLock, face: &mut Arc, expr_id: ExprId) { let wtables = zwrite!(tables.tables); match get_mut_unchecked(face).remote_mappings.remove(&expr_id) { Some(mut res) => Resource::clean(&mut res), diff --git a/zenoh/src/net/routing/router.rs b/zenoh/src/net/routing/router.rs index a7993e7d13..dbf687ba79 100644 --- a/zenoh/src/net/routing/router.rs +++ b/zenoh/src/net/routing/router.rs @@ -17,6 +17,8 @@ pub use super::pubsub::*; pub use super::queries::*; pub use super::resource::*; use super::runtime::Runtime; +use crate::net::codec::Zenoh080Routing; +use crate::net::protocol::linkstate::LinkStateList; use async_std::task::JoinHandle; use std::any::Any; use std::collections::hash_map::DefaultHasher; @@ -27,12 +29,13 @@ use std::sync::{Arc, Weak}; use std::sync::{Mutex, RwLock}; use std::time::Duration; use uhlc::HLC; -use zenoh_config::whatami::WhatAmIMatcher; use zenoh_link::Link; -use zenoh_protocol::{ - core::{WhatAmI, ZInt, ZenohId}, - zenoh::{ZenohBody, ZenohMessage}, -}; +use zenoh_protocol::common::ZExtBody; +use zenoh_protocol::core::{ExprId, WhatAmI, WhatAmIMatcher, ZenohId}; +use zenoh_protocol::network::oam::id::OAM_LINKSTATE; +use zenoh_protocol::network::{Mapping, NetworkBody, NetworkMessage}; +#[cfg(feature = "stats")] +use zenoh_transport::stats::TransportStats; use zenoh_transport::{ DeMux, DummyPrimitives, McastMux, Mux, Primitives, TransportMulticast, TransportPeer, TransportPeerEventHandler, TransportUnicast, @@ -146,11 +149,12 @@ impl Tables { pub(crate) fn get_mapping<'a>( &'a self, face: &'a FaceState, - expr_id: &ZInt, + expr_id: &ExprId, + mapping: Mapping, ) -> Option<&'a Arc> { match expr_id { 0 => Some(&self.root_res), - expr_id => face.get_mapping(expr_id), + expr_id => face.get_mapping(expr_id, mapping), } } @@ -254,6 +258,7 @@ impl Tables { &mut self, zid: ZenohId, whatami: WhatAmI, + #[cfg(feature = "stats")] stats: Arc, primitives: Arc, link_id: usize, ) -> Weak { @@ -262,7 +267,18 @@ impl Tables { let mut newface = self .faces .entry(fid) - .or_insert_with(|| FaceState::new(fid, zid, whatami, primitives.clone(), link_id, None)) + .or_insert_with(|| { + FaceState::new( + fid, + zid, + whatami, + #[cfg(feature = "stats")] + Some(stats), + primitives.clone(), + link_id, + None, + ) + }) .clone(); log::debug!("New {}", newface); @@ -278,7 +294,30 @@ impl Tables { whatami: WhatAmI, primitives: Arc, ) -> Weak { - self.open_net_face(zid, whatami, primitives, 0) + let fid = self.face_counter; + self.face_counter += 1; + let mut newface = self + .faces + .entry(fid) + .or_insert_with(|| { + FaceState::new( + fid, + zid, + whatami, + #[cfg(feature = "stats")] + None, + primitives.clone(), + 0, + None, + ) + }) + .clone(); + log::debug!("New {}", newface); + + pubsub_new_face(self, &mut newface); + queries_new_face(self, &mut newface); + + Arc::downgrade(&newface) } fn compute_routes(&mut self, res: &mut Arc) { @@ -574,6 +613,8 @@ impl Router { .open_net_face( transport.get_zid().unwrap(), whatami, + #[cfg(feature = "stats")] + transport.get_stats().unwrap(), Arc::new(Mux::new(transport)), link_id, ) @@ -608,6 +649,8 @@ impl Router { fid, ZenohId::from_str("1").unwrap(), WhatAmI::Peer, + #[cfg(feature = "stats")] + None, Arc::new(McastMux::new(transport.clone())), 0, Some(transport), @@ -631,6 +674,8 @@ impl Router { fid, peer.zid, WhatAmI::Client, // Quick hack + #[cfg(feature = "stats")] + Some(transport.get_stats().unwrap()), Arc::new(DummyPrimitives), 0, Some(transport), @@ -666,88 +711,107 @@ impl LinkStateInterceptor { } impl TransportPeerEventHandler for LinkStateInterceptor { - fn handle_message(&self, msg: ZenohMessage) -> ZResult<()> { + fn handle_message(&self, msg: NetworkMessage) -> ZResult<()> { log::trace!("Recv {:?}", msg); match msg.body { - ZenohBody::LinkStateList(list) => { - if let Ok(zid) = self.transport.get_zid() { - let ctrl_lock = zlock!(self.tables.ctrl_lock); - let mut tables = zwrite!(self.tables.tables); - let whatami = self.transport.get_whatami()?; - match (tables.whatami, whatami) { - (WhatAmI::Router, WhatAmI::Router) => { - for (_, removed_node) in tables - .routers_net - .as_mut() - .unwrap() - .link_states(list.link_states, zid) - .removed_nodes - { - pubsub_remove_node(&mut tables, &removed_node.zid, WhatAmI::Router); - queries_remove_node( - &mut tables, - &removed_node.zid, - WhatAmI::Router, - ); - } - - if tables.full_net(WhatAmI::Peer) { - tables.shared_nodes = shared_nodes( - tables.routers_net.as_ref().unwrap(), - tables.peers_net.as_ref().unwrap(), - ); - } - - tables.schedule_compute_trees(self.tables.clone(), WhatAmI::Router); - } - (WhatAmI::Router, WhatAmI::Peer) - | (WhatAmI::Peer, WhatAmI::Router) - | (WhatAmI::Peer, WhatAmI::Peer) => { - if let Some(net) = tables.peers_net.as_mut() { - let changes = net.link_states(list.link_states, zid); - if tables.full_net(WhatAmI::Peer) { - for (_, removed_node) in changes.removed_nodes { + NetworkBody::OAM(oam) => { + if oam.id == OAM_LINKSTATE { + if let ZExtBody::ZBuf(buf) = oam.body { + if let Ok(zid) = self.transport.get_zid() { + use zenoh_buffers::reader::HasReader; + use zenoh_codec::RCodec; + let codec = Zenoh080Routing::new(); + let mut reader = buf.reader(); + let list: LinkStateList = codec.read(&mut reader).unwrap(); + + let ctrl_lock = zlock!(self.tables.ctrl_lock); + let mut tables = zwrite!(self.tables.tables); + let whatami = self.transport.get_whatami()?; + match (tables.whatami, whatami) { + (WhatAmI::Router, WhatAmI::Router) => { + for (_, removed_node) in tables + .routers_net + .as_mut() + .unwrap() + .link_states(list.link_states, zid) + .removed_nodes + { pubsub_remove_node( &mut tables, &removed_node.zid, - WhatAmI::Peer, + WhatAmI::Router, ); queries_remove_node( &mut tables, &removed_node.zid, - WhatAmI::Peer, + WhatAmI::Router, ); } - if tables.whatami == WhatAmI::Router { + if tables.full_net(WhatAmI::Peer) { tables.shared_nodes = shared_nodes( tables.routers_net.as_ref().unwrap(), tables.peers_net.as_ref().unwrap(), ); } - tables - .schedule_compute_trees(self.tables.clone(), WhatAmI::Peer); - } else { - for (_, updated_node) in changes.updated_nodes { - pubsub_linkstate_change( - &mut tables, - &updated_node.zid, - &updated_node.links, - ); - queries_linkstate_change( - &mut tables, - &updated_node.zid, - &updated_node.links, - ); + tables.schedule_compute_trees( + self.tables.clone(), + WhatAmI::Router, + ); + } + (WhatAmI::Router, WhatAmI::Peer) + | (WhatAmI::Peer, WhatAmI::Router) + | (WhatAmI::Peer, WhatAmI::Peer) => { + if let Some(net) = tables.peers_net.as_mut() { + let changes = net.link_states(list.link_states, zid); + if tables.full_net(WhatAmI::Peer) { + for (_, removed_node) in changes.removed_nodes { + pubsub_remove_node( + &mut tables, + &removed_node.zid, + WhatAmI::Peer, + ); + queries_remove_node( + &mut tables, + &removed_node.zid, + WhatAmI::Peer, + ); + } + + if tables.whatami == WhatAmI::Router { + tables.shared_nodes = shared_nodes( + tables.routers_net.as_ref().unwrap(), + tables.peers_net.as_ref().unwrap(), + ); + } + + tables.schedule_compute_trees( + self.tables.clone(), + WhatAmI::Peer, + ); + } else { + for (_, updated_node) in changes.updated_nodes { + pubsub_linkstate_change( + &mut tables, + &updated_node.zid, + &updated_node.links, + ); + queries_linkstate_change( + &mut tables, + &updated_node.zid, + &updated_node.links, + ); + } + } } } - } + _ => (), + }; + drop(tables); + drop(ctrl_lock); } - _ => (), - }; - drop(tables); - drop(ctrl_lock); + } } Ok(()) diff --git a/zenoh/src/net/runtime/adminspace.rs b/zenoh/src/net/runtime/adminspace.rs index ae818b7995..23bb47d9b9 100644 --- a/zenoh/src/net/runtime/adminspace.rs +++ b/zenoh/src/net/runtime/adminspace.rs @@ -14,7 +14,7 @@ use super::routing::face::Face; use super::Runtime; use crate::key_expr::KeyExpr; use crate::plugins::sealed as plugins; -use crate::prelude::sync::Sample; +use crate::prelude::sync::{Sample, SyncResolve}; use crate::queryable::Query; use crate::queryable::QueryInner; use crate::value::Value; @@ -26,15 +26,16 @@ use std::convert::TryFrom; use std::convert::TryInto; use std::sync::Arc; use std::sync::Mutex; -use zenoh_buffers::{SplitBuffer, ZBuf}; +use zenoh_buffers::SplitBuffer; use zenoh_config::ValidatedMap; -use zenoh_core::SyncResolve; use zenoh_protocol::{ - core::{ - key_expr::OwnedKeyExpr, Channel, CongestionControl, ConsolidationMode, KnownEncoding, - QueryTarget, QueryableInfo, SampleKind, SubInfo, WireExpr, ZInt, ZenohId, EMPTY_EXPR_ID, + core::{key_expr::OwnedKeyExpr, ExprId, KnownEncoding, WireExpr, ZenohId, EMPTY_EXPR_ID}, + network::{ + declare::{queryable::ext::QueryableInfo, subscriber::ext::SubscriberInfo}, + ext, Declare, DeclareBody, DeclareQueryable, DeclareSubscriber, Push, Request, Response, + ResponseFinal, }, - zenoh::{DataInfo, QueryBody, RoutingContext}, + zenoh::{PushBody, RequestBody}, }; use zenoh_result::ZResult; use zenoh_transport::{Primitives, TransportUnicast}; @@ -52,7 +53,7 @@ type Handler = Arc; pub struct AdminSpace { zid: ZenohId, primitives: Mutex>>, - mappings: Mutex>, + mappings: Mutex>, handlers: HashMap, context: Arc, } @@ -219,20 +220,30 @@ impl AdminSpace { let primitives = runtime.router.new_primitives(admin.clone()); zlock!(admin.primitives).replace(primitives.clone()); - primitives.decl_queryable( - &[&root_key, "/**"].concat().into(), - &QueryableInfo { - complete: 0, - distance: 0, - }, - None, - ); + primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareQueryable(DeclareQueryable { + id: 0, // TODO + wire_expr: [&root_key, "/**"].concat().into(), + ext_info: QueryableInfo { + complete: 0, + distance: 0, + }, + }), + }); - primitives.decl_subscriber( - &[&root_key, "/config/**"].concat().into(), - &SubInfo::default(), - None, - ); + primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareSubscriber(DeclareSubscriber { + id: 0, // TODO + wire_expr: [&root_key, "/config/**"].concat().into(), + ext_info: SubscriberInfo::default(), + }), + }); } pub fn key_expr_to_string<'a>(&self, key_expr: &'a WireExpr) -> ZResult> { @@ -253,102 +264,38 @@ impl AdminSpace { } impl Primitives for AdminSpace { - fn decl_resource(&self, expr_id: ZInt, key_expr: &WireExpr) { - trace!("recv Resource {} {:?}", expr_id, key_expr); - match self.key_expr_to_string(key_expr) { - Ok(s) => { - zlock!(self.mappings).insert(expr_id, s.into()); + fn send_declare(&self, msg: Declare) { + log::trace!("Recv declare {:?}", msg); + if let DeclareBody::DeclareKeyExpr(m) = msg.body { + match self.key_expr_to_string(&m.wire_expr) { + Ok(s) => { + zlock!(self.mappings).insert(m.id, s.into()); + } + Err(e) => error!("Unknown expr_id {}! ({})", m.id, e), } - Err(e) => error!("Unknown expr_id {}! ({})", expr_id, e), } } - fn forget_resource(&self, _expr_id: ZInt) { - trace!("recv Forget Resource {}", _expr_id); - } - - fn decl_publisher(&self, _key_expr: &WireExpr, _routing_context: Option) { - trace!("recv Publisher {:?}", _key_expr); - } - - fn forget_publisher(&self, _key_expr: &WireExpr, _routing_context: Option) { - trace!("recv Forget Publisher {:?}", _key_expr); - } - - fn decl_subscriber( - &self, - _key_expr: &WireExpr, - _sub_info: &SubInfo, - _routing_context: Option, - ) { - trace!("recv Subscriber {:?} , {:?}", _key_expr, _sub_info); - } - - fn forget_subscriber(&self, _key_expr: &WireExpr, _routing_context: Option) { - trace!("recv Forget Subscriber {:?}", _key_expr); - } - - fn decl_queryable( - &self, - _key_expr: &WireExpr, - _qabl_info: &QueryableInfo, - _routing_context: Option, - ) { - trace!("recv Queryable {:?}", _key_expr); - } - - fn forget_queryable(&self, _key_expr: &WireExpr, _routing_context: Option) { - trace!("recv Forget Queryable {:?}", _key_expr); - } - - fn send_data( - &self, - key_expr: &WireExpr, - payload: ZBuf, - channel: Channel, - congestion_control: CongestionControl, - data_info: Option, - _routing_context: Option, - ) { - trace!( - "recv Data {:?} {:?} {:?} {:?} {:?}", - key_expr, - payload, - channel, - congestion_control, - data_info, - ); - + fn send_push(&self, msg: Push) { + trace!("recv Push {:?}", msg); { let conf = self.context.runtime.config.lock(); if !conf.adminspace.permissions().write { log::error!( "Received PUT on '{}' but adminspace.permissions.write=false in configuration", - key_expr + msg.wire_expr ); return; } } - if let Some(key) = key_expr + if let Some(key) = msg + .wire_expr .as_str() .strip_prefix(&format!("@/router/{}/config/", &self.context.zid_str)) { - if let Some(DataInfo { - kind: SampleKind::Delete, - .. - }) = data_info - { - log::trace!( - "Deleting conf value /@/router/{}/config/{}", - &self.context.zid_str, - key - ); - if let Err(e) = self.context.runtime.config.remove(key) { - log::error!("Error deleting conf value {} : {}", key_expr, e) - } - } else { - match std::str::from_utf8(&payload.contiguous()) { + match msg.payload { + PushBody::Put(put) => match std::str::from_utf8(&put.payload.contiguous()) { Ok(json) => { log::trace!( "Insert conf value /@/router/{}/config/{} : {}", @@ -367,109 +314,83 @@ impl Primitives for AdminSpace { "Received non utf8 conf value on /@/router/{}/config/{} : {}", &self.context.zid_str, key, e ), + }, + PushBody::Del(_) => { + log::trace!( + "Deleting conf value /@/router/{}/config/{}", + &self.context.zid_str, + key + ); + if let Err(e) = self.context.runtime.config.remove(key) { + log::error!("Error deleting conf value {} : {}", msg.wire_expr, e) + } } } } } - fn send_query( - &self, - key_expr: &WireExpr, - parameters: &str, - qid: ZInt, - target: QueryTarget, - _consolidation: ConsolidationMode, - body: Option, - _routing_context: Option, - ) { - trace!( - "recv Query {:?} {:?} {:?} {:?}", - key_expr, - parameters, - target, - _consolidation - ); - let primitives = zlock!(self.primitives).as_ref().unwrap().clone(); - - { - let conf = self.context.runtime.config.lock(); - if !conf.adminspace.permissions().read { - log::error!( - "Received GET on '{}' but adminspace.permissions.read=false in configuration", - key_expr - ); - primitives.send_reply_final(qid); - return; + fn send_request(&self, msg: Request) { + trace!("recv Request {:?}", msg); + if let RequestBody::Query(query) = msg.payload { + let primitives = zlock!(self.primitives).as_ref().unwrap().clone(); + { + let conf = self.context.runtime.config.lock(); + if !conf.adminspace.permissions().read { + log::error!( + "Received GET on '{}' but adminspace.permissions.read=false in configuration", + msg.wire_expr + ); + primitives.send_response_final(ResponseFinal { + rid: msg.id, + ext_qos: ext::QoSType::response_final_default(), + ext_tstamp: None, + }); + return; + } } - } - let key_expr = match self.key_expr_to_string(key_expr) { - Ok(key_expr) => key_expr.into_owned(), - Err(e) => { - log::error!("Unknown KeyExpr: {}", e); - primitives.send_reply_final(qid); - return; - } - }; - - let zid = self.zid; - let parameters = parameters.to_owned(); - let query = Query { - inner: Arc::new(QueryInner { - key_expr: key_expr.clone(), - parameters, - value: body.map(|b| { - Value::from(b.payload).encoding(b.data_info.encoding.unwrap_or_default()) + let key_expr = match self.key_expr_to_string(&msg.wire_expr) { + Ok(key_expr) => key_expr.into_owned(), + Err(e) => { + log::error!("Unknown KeyExpr: {}", e); + primitives.send_response_final(ResponseFinal { + rid: msg.id, + ext_qos: ext::QoSType::response_final_default(), + ext_tstamp: None, + }); + return; + } + }; + + let zid = self.zid; + let parameters = query.parameters.to_owned(); + let query = Query { + inner: Arc::new(QueryInner { + key_expr: key_expr.clone(), + parameters, + value: query + .ext_body + .map(|b| Value::from(b.payload).encoding(b.encoding)), + qid: msg.id, + zid, + primitives, }), - qid, - zid, - primitives, - }), - }; + }; - for (key, handler) in &self.handlers { - if key_expr.intersects(key) { - handler(&self.context, query.clone()); + for (key, handler) in &self.handlers { + if key_expr.intersects(key) { + handler(&self.context, query.clone()); + } } } } - fn send_reply_data( - &self, - qid: ZInt, - replier_id: ZenohId, - key_expr: WireExpr, - info: Option, - payload: ZBuf, - ) { - trace!( - "recv ReplyData {:?} {:?} {:?} {:?} {:?}", - qid, - replier_id, - key_expr, - info, - payload - ); - } - - fn send_reply_final(&self, qid: ZInt) { - trace!("recv ReplyFinal {:?}", qid); + fn send_response(&self, msg: Response) { + trace!("recv Response {:?}", msg); } - fn send_pull( - &self, - _is_final: bool, - _key_expr: &WireExpr, - _pull_id: ZInt, - _max_samples: &Option, - ) { - trace!( - "recv Pull {:?} {:?} {:?} {:?}", - _is_final, - _key_expr, - _pull_id, - _max_samples - ); + fn send_response_final(&self, msg: ResponseFinal) { + trace!("recv ResponseFinal {:?}", msg); } fn send_close(&self) { @@ -495,7 +416,7 @@ fn router_data(context: &AdminContext, query: Query) { let locators: Vec = transport_mgr .get_locators() .iter() - .map(|locator| json!(locator.to_string())) + .map(|locator| json!(locator.as_str())) .collect(); // transports info @@ -518,14 +439,13 @@ fn router_data(context: &AdminContext, query: Query) { "stats".to_string(), transport .get_stats() - .map_or_else(|_| json!({}), |p| json!(p)), + .map_or_else(|_| json!({}), |p| json!(p.report())), ); } } json }; - let transports: Vec = transport_mgr - .get_transports() + let transports: Vec = task::block_on(transport_mgr.get_transports_unicast()) .iter() .map(transport_to_json) .collect(); diff --git a/zenoh/src/net/runtime/mod.rs b/zenoh/src/net/runtime/mod.rs index b1c78e5ac0..92d369e998 100644 --- a/zenoh/src/net/runtime/mod.rs +++ b/zenoh/src/net/runtime/mod.rs @@ -37,10 +37,8 @@ use stop_token::future::FutureExt; use stop_token::{StopSource, TimedOutError}; use uhlc::{HLCBuilder, HLC}; use zenoh_link::{EndPoint, Link}; -use zenoh_protocol::{ - core::{whatami::WhatAmIMatcher, Locator, WhatAmI, ZenohId}, - zenoh::{ZenohBody, ZenohMessage}, -}; +use zenoh_protocol::core::{whatami::WhatAmIMatcher, Locator, WhatAmI, ZenohId}; +use zenoh_protocol::network::{NetworkBody, NetworkMessage}; use zenoh_result::{bail, ZResult}; use zenoh_sync::get_mut_unchecked; use zenoh_transport::{ @@ -278,25 +276,20 @@ pub(super) struct RuntimeSession { } impl TransportPeerEventHandler for RuntimeSession { - fn handle_message(&self, mut msg: ZenohMessage) -> ZResult<()> { + fn handle_message(&self, msg: NetworkMessage) -> ZResult<()> { // critical path shortcut - if let ZenohBody::Data(data) = msg.body { - if data.reply_context.is_none() { - let face = &self.main_handler.face.state; - full_reentrant_route_data( - &self.main_handler.tables.tables, - face, - &data.key, - msg.channel, - data.congestion_control, - data.data_info, - data.payload, - msg.routing_context, - ); - return Ok(()); - } else { - msg.body = ZenohBody::Data(data); - } + if let NetworkBody::Push(data) = msg.body { + let face = &self.main_handler.face.state; + + full_reentrant_route_data( + &self.main_handler.tables.tables, + face, + &data.wire_expr, + data.ext_qos, + data.payload, + data.ext_nodeid.node_id.into(), + ); + return Ok(()); } self.main_handler.handle_message(msg) @@ -381,7 +374,7 @@ pub(super) struct RuntimeMuticastSession { } impl TransportPeerEventHandler for RuntimeMuticastSession { - fn handle_message(&self, msg: ZenohMessage) -> ZResult<()> { + fn handle_message(&self, msg: NetworkMessage) -> ZResult<()> { self.main_handler.handle_message(msg) } diff --git a/zenoh/src/net/runtime/orchestrator.rs b/zenoh/src/net/runtime/orchestrator.rs index 3dd3af9e69..3aef55b4fe 100644 --- a/zenoh/src/net/runtime/orchestrator.rs +++ b/zenoh/src/net/runtime/orchestrator.rs @@ -20,15 +20,14 @@ use std::net::{IpAddr, Ipv6Addr, SocketAddr}; use std::time::Duration; use zenoh_buffers::reader::DidntRead; use zenoh_buffers::{reader::HasReader, writer::HasWriter}; -use zenoh_codec::{RCodec, WCodec, Zenoh060}; -use zenoh_config::{unwrap_or_default, EndPoint, ModeDependent}; +use zenoh_codec::{RCodec, WCodec, Zenoh080}; +use zenoh_config::{unwrap_or_default, ModeDependent}; use zenoh_link::{Locator, LocatorInspector}; use zenoh_protocol::{ - core::{whatami::WhatAmIMatcher, WhatAmI, ZenohId}, + core::{whatami::WhatAmIMatcher, EndPoint, WhatAmI, ZenohId}, scouting::{Hello, Scout, ScoutingBody, ScoutingMessage}, }; use zenoh_result::{bail, zerror, ZResult}; -use zenoh_transport::TransportUnicast; const RCV_BUF_SIZE: usize = u16::MAX as usize; const SCOUT_INITIAL_PERIOD: Duration = Duration::from_millis(1_000); @@ -93,7 +92,7 @@ impl Runtime { for locator in &peers { match self .manager() - .open_transport(locator.clone()) + .open_transport_unicast(locator.clone()) .timeout(CONNECTION_TIMEOUT) .await { @@ -245,7 +244,7 @@ impl Runtime { pub(crate) async fn update_peers(&self) -> ZResult<()> { let peers = { self.config.lock().connect().endpoints().clone() }; - let tranports = self.manager().get_transports(); + let tranports = self.manager().get_transports_unicast().await; if self.whatami == WhatAmI::Client { for transport in tranports { @@ -501,7 +500,7 @@ impl Runtime { } else { match self .manager() - .open_transport(endpoint) + .open_transport_unicast(endpoint) .timeout(CONNECTION_TIMEOUT) .await { @@ -557,10 +556,15 @@ impl Runtime { let send = async { let mut delay = SCOUT_INITIAL_PERIOD; - let scout = ScoutingMessage::make_scout(Some(matcher), true, None); + let scout: ScoutingMessage = Scout { + version: zenoh_protocol::VERSION, + what: matcher, + zid: None, + } + .into(); let mut wbuf = vec![]; let mut writer = wbuf.writer(); - let codec = Zenoh060::default(); + let codec = Zenoh080::new(); codec.write(&mut writer, &scout).unwrap(); loop { @@ -602,7 +606,7 @@ impl Runtime { match socket.recv_from(&mut buf).await { Ok((n, peer)) => { let mut reader = buf.as_slice()[..n].reader(); - let codec = Zenoh060::default(); + let codec = Zenoh080::new(); let res: Result = codec.read(&mut reader); if let Ok(msg) = res { log::trace!("Received {:?} from {}", msg.body, peer); @@ -632,40 +636,84 @@ impl Runtime { async_std::prelude::FutureExt::race(send, recvs).await; } - async fn connect(&self, locators: &[Locator]) -> Option { + #[must_use] + async fn connect(&self, zid: &ZenohId, locators: &[Locator]) -> bool { + const ERR: &str = "Unable to connect to newly scouted peer "; + + let inspector = LocatorInspector::default(); for locator in locators { - let endpoint = locator.clone().into(); - match self - .manager() - .open_transport(endpoint) - .timeout(CONNECTION_TIMEOUT) - .await - { - Ok(Ok(transport)) => return Some(transport), - Ok(Err(e)) => log::trace!("Unable to connect to {}! {}", locator, e), - Err(e) => log::trace!("Unable to connect to {}! {}", locator, e), + let is_multicast = match inspector.is_multicast(locator).await { + Ok(im) => im, + Err(e) => { + log::trace!("{} {} on {}: {}", ERR, zid, locator, e); + continue; + } + }; + + let endpoint = locator.to_owned().into(); + let manager = self.manager(); + if is_multicast { + match manager + .open_transport_multicast(endpoint) + .timeout(CONNECTION_TIMEOUT) + .await + { + Ok(Ok(transport)) => { + log::debug!( + "Successfully connected to newly scouted peer: {:?}", + transport + ); + return true; + } + Ok(Err(e)) => log::trace!("{} {} on {}: {}", ERR, zid, locator, e), + Err(e) => log::trace!("{} {} on {}: {}", ERR, zid, locator, e), + } + } else { + match manager + .open_transport_unicast(endpoint) + .timeout(CONNECTION_TIMEOUT) + .await + { + Ok(Ok(transport)) => { + log::debug!( + "Successfully connected to newly scouted peer: {:?}", + transport + ); + return true; + } + Ok(Err(e)) => log::trace!("{} {} on {}: {}", ERR, zid, locator, e), + Err(e) => log::trace!("{} {} on {}: {}", ERR, zid, locator, e), + } } } - None + + log::warn!( + "Unable to connect to any locator of scouted peer {}: {:?}", + zid, + locators + ); + false } pub async fn connect_peer(&self, zid: &ZenohId, locators: &[Locator]) { - if zid != &self.manager().zid() { - if self.manager().get_transport(zid).is_none() { - log::debug!("Try to connect to peer {} via any of {:?}", zid, locators); - if let Some(transport) = self.connect(locators).await { - log::debug!( - "Successfully connected to newly scouted peer {} via {:?}", - zid, - transport - ); - } else { - log::warn!( - "Unable to connect any locator of scouted peer {}: {:?}", - zid, - locators - ); + let manager = self.manager(); + if zid != &manager.zid() { + let has_unicast = manager.get_transport_unicast(zid).await.is_some(); + let has_multicast = { + let mut hm = manager.get_transport_multicast(zid).await.is_some(); + for t in manager.get_transports_multicast().await { + if let Ok(l) = t.get_link() { + if let Some(g) = l.group.as_ref() { + hm |= locators.iter().any(|l| l == g); + } + } } + hm + }; + + if !has_unicast && !has_multicast { + log::debug!("Try to connect to peer {} via any of {:?}", zid, locators); + let _ = self.connect(zid, locators).await; } else { log::trace!("Already connected scouted peer: {}", zid); } @@ -683,15 +731,9 @@ impl Runtime { Runtime::scout(sockets, what, addr, move |hello| async move { log::info!("Found {:?}", hello); if !hello.locators.is_empty() { - if let Some(transport) = self.connect(&hello.locators).await { - log::debug!( - "Successfully connected to newly scouted {:?} via {:?}", - hello, - transport - ); + if self.connect(&hello.zid, &hello.locators).await { return Loop::Break; } - log::warn!("Unable to connect to scouted {:?}", hello); } else { log::warn!("Received Hello with no locators: {:?}", hello); } @@ -714,17 +756,10 @@ impl Runtime { addr: &SocketAddr, ) { Runtime::scout(ucast_sockets, what, addr, move |hello| async move { - match &hello.zid { - Some(zid) => { - if !hello.locators.is_empty() { - self.connect_peer(zid, &hello.locators).await - } else { - log::warn!("Received Hello with no locators: {:?}", hello); - } - } - None => { - log::warn!("Received Hello with no zid: {:?}", hello); - } + if !hello.locators.is_empty() { + self.connect_peer(&hello.zid, &hello.locators).await + } else { + log::warn!("Received Hello with no locators: {:?}", hello); } Loop::Continue }) @@ -769,31 +804,24 @@ impl Runtime { } let mut reader = buf.as_slice()[..n].reader(); - let codec = Zenoh060::default(); + let codec = Zenoh080::new(); let res: Result = codec.read(&mut reader); if let Ok(msg) = res { log::trace!("Received {:?} from {}", msg.body, peer); - if let ScoutingBody::Scout(Scout { - what, zid_request, .. - }) = &msg.body - { - let what = what.or(Some(WhatAmI::Router.into())).unwrap(); + if let ScoutingBody::Scout(Scout { what, .. }) = &msg.body { if what.matches(self.whatami) { let mut wbuf = vec![]; let mut writer = wbuf.writer(); - let codec = Zenoh060::default(); + let codec = Zenoh080::new(); - let zid = if *zid_request { - Some(self.manager().zid()) - } else { - None - }; - let hello = ScoutingMessage::make_hello( + let zid = self.manager().zid(); + let hello: ScoutingMessage = Hello { + version: zenoh_protocol::VERSION, + whatami: self.whatami, zid, - Some(self.whatami), - Some(self.get_locators()), - None, - ); + locators: self.get_locators(), + } + .into(); let socket = get_best_match(&peer.ip(), ucast_sockets).unwrap(); log::trace!( "Send {:?} to {} on interface {}", diff --git a/zenoh/src/net/tests/tables.rs b/zenoh/src/net/tests/tables.rs index 91538a7262..5dadf8d8a9 100644 --- a/zenoh/src/net/tests/tables.rs +++ b/zenoh/src/net/tests/tables.rs @@ -17,16 +17,16 @@ use std::sync::{Arc, Mutex, RwLock}; use std::time::Duration; use uhlc::HLC; use zenoh_buffers::ZBuf; -use zenoh_config::ZN_QUERIES_DEFAULT_TIMEOUT_DEFAULT; +use zenoh_config::defaults::queries_default_timeout; use zenoh_core::zlock; -use zenoh_protocol::{ - core::{ - key_expr::keyexpr, Channel, CongestionControl, ConsolidationMode, QueryTarget, - QueryableInfo, Reliability, SubInfo, SubMode, WhatAmI, WireExpr, ZInt, ZenohId, - EMPTY_EXPR_ID, - }, - zenoh::{DataInfo, QueryBody, RoutingContext}, +use zenoh_protocol::core::Encoding; +use zenoh_protocol::core::{ + key_expr::keyexpr, ExprId, Reliability, WhatAmI, WireExpr, ZenohId, EMPTY_EXPR_ID, }; +use zenoh_protocol::network::declare::subscriber::ext::SubscriberInfo; +use zenoh_protocol::network::declare::Mode; +use zenoh_protocol::network::{ext, Declare, DeclareBody, DeclareKeyExpr}; +use zenoh_protocol::zenoh::{PushBody, Put}; use zenoh_transport::{DummyPrimitives, Primitives}; #[test] @@ -38,7 +38,7 @@ fn base_test() { Some(Arc::new(HLC::default())), false, true, - Duration::from_millis(ZN_QUERIES_DEFAULT_TIMEOUT_DEFAULT.parse().unwrap()), + Duration::from_millis(queries_default_timeout), )), ctrl_lock: Mutex::new(()), queries_lock: RwLock::new(()), @@ -63,9 +63,9 @@ fn base_test() { &"one/deux/trois".into(), ); - let sub_info = SubInfo { + let sub_info = SubscriberInfo { reliability: Reliability::Reliable, - mode: SubMode::Push, + mode: Mode::Push, }; declare_client_subscription( &tables, @@ -139,7 +139,7 @@ fn match_test() { Some(Arc::new(HLC::default())), false, true, - Duration::from_millis(ZN_QUERIES_DEFAULT_TIMEOUT_DEFAULT.parse().unwrap()), + Duration::from_millis(queries_default_timeout), )), ctrl_lock: Mutex::new(()), queries_lock: RwLock::new(()), @@ -185,7 +185,7 @@ fn clean_test() { Some(Arc::new(HLC::default())), false, true, - Duration::from_millis(ZN_QUERIES_DEFAULT_TIMEOUT_DEFAULT.parse().unwrap()), + Duration::from_millis(queries_default_timeout), )), ctrl_lock: Mutex::new(()), queries_lock: RwLock::new(()), @@ -249,9 +249,9 @@ fn clean_test() { let res1 = optres1.unwrap(); assert!(res1.upgrade().is_some()); - let sub_info = SubInfo { + let sub_info = SubscriberInfo { reliability: Reliability::Reliable, - mode: SubMode::Push, + mode: Mode::Push, }; declare_client_subscription( @@ -375,7 +375,7 @@ fn clean_test() { pub struct ClientPrimitives { data: std::sync::Mutex>>, - mapping: std::sync::Mutex>, + mapping: std::sync::Mutex>, } impl ClientPrimitives { @@ -425,79 +425,28 @@ impl ClientPrimitives { } impl Primitives for ClientPrimitives { - fn decl_resource(&self, expr_id: ZInt, key_expr: &WireExpr) { - let name = self.get_name(key_expr); - zlock!(self.mapping).insert(expr_id, name); + fn send_declare(&self, msg: zenoh_protocol::network::Declare) { + match msg.body { + DeclareBody::DeclareKeyExpr(d) => { + let name = self.get_name(&d.wire_expr); + zlock!(self.mapping).insert(d.id, name); + } + DeclareBody::UndeclareKeyExpr(u) => { + zlock!(self.mapping).remove(&u.id); + } + _ => (), + } } - fn forget_resource(&self, expr_id: ZInt) { - zlock!(self.mapping).remove(&expr_id); + fn send_push(&self, msg: zenoh_protocol::network::Push) { + *zlock!(self.data) = Some(msg.wire_expr.to_owned()); } - fn decl_publisher(&self, _key_expr: &WireExpr, _routing_context: Option) {} - fn forget_publisher(&self, _key_expr: &WireExpr, _routing_context: Option) {} + fn send_request(&self, _msg: zenoh_protocol::network::Request) {} - fn decl_subscriber( - &self, - _key_expr: &WireExpr, - _sub_info: &SubInfo, - _routing_context: Option, - ) { - } - fn forget_subscriber(&self, _key_expr: &WireExpr, _routing_context: Option) {} - - fn decl_queryable( - &self, - _key_expr: &WireExpr, - _qabl_info: &QueryableInfo, - _routing_context: Option, - ) { - } - fn forget_queryable(&self, _key_expr: &WireExpr, _routing_context: Option) {} - - fn send_data( - &self, - key_expr: &WireExpr, - _payload: ZBuf, - _channel: Channel, - _congestion_control: CongestionControl, - _info: Option, - _routing_context: Option, - ) { - *zlock!(self.data) = Some(key_expr.to_owned()); - } + fn send_response(&self, _msg: zenoh_protocol::network::Response) {} - fn send_query( - &self, - _key_expr: &WireExpr, - _parameters: &str, - _qid: ZInt, - _target: QueryTarget, - _consolidation: ConsolidationMode, - _body: Option, - _routing_context: Option, - ) { - } - - fn send_reply_data( - &self, - _qid: ZInt, - _replier_id: ZenohId, - _key_expr: WireExpr, - _info: Option, - _payload: ZBuf, - ) { - } - fn send_reply_final(&self, _qid: ZInt) {} - - fn send_pull( - &self, - _is_final: bool, - _key_expr: &WireExpr, - _pull_id: ZInt, - _max_samples: &Option, - ) { - } + fn send_response_final(&self, _msg: zenoh_protocol::network::ResponseFinal) {} fn send_close(&self) {} } @@ -511,15 +460,15 @@ fn client_test() { Some(Arc::new(HLC::default())), false, true, - Duration::from_millis(ZN_QUERIES_DEFAULT_TIMEOUT_DEFAULT.parse().unwrap()), + Duration::from_millis(queries_default_timeout), )), ctrl_lock: Mutex::new(()), queries_lock: RwLock::new(()), }; - let sub_info = SubInfo { + let sub_info = SubscriberInfo { reliability: Reliability::Reliable, - mode: SubMode::Push, + mode: Mode::Push, }; let primitives0 = Arc::new(ClientPrimitives::new()); @@ -535,7 +484,15 @@ fn client_test() { 11, &"test/client".into(), ); - primitives0.decl_resource(11, &"test/client".into()); + primitives0.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareKeyExpr(DeclareKeyExpr { + id: 11, + wire_expr: "test/client".into(), + }), + }); declare_client_subscription( &tables, zread!(tables.tables), @@ -549,7 +506,15 @@ fn client_test() { 12, &WireExpr::from(11).with_suffix("/z1_pub1"), ); - primitives0.decl_resource(12, &WireExpr::from(11).with_suffix("/z1_pub1")); + primitives0.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareKeyExpr(DeclareKeyExpr { + id: 12, + wire_expr: WireExpr::from(11).with_suffix("/z1_pub1"), + }), + }); let primitives1 = Arc::new(ClientPrimitives::new()); let face1 = zwrite!(tables.tables).open_face( @@ -563,7 +528,15 @@ fn client_test() { 21, &"test/client".into(), ); - primitives1.decl_resource(21, &"test/client".into()); + primitives1.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareKeyExpr(DeclareKeyExpr { + id: 21, + wire_expr: "test/client".into(), + }), + }); declare_client_subscription( &tables, zread!(tables.tables), @@ -577,7 +550,15 @@ fn client_test() { 22, &WireExpr::from(21).with_suffix("/z2_pub1"), ); - primitives1.decl_resource(22, &WireExpr::from(21).with_suffix("/z2_pub1")); + primitives1.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareKeyExpr(DeclareKeyExpr { + id: 22, + wire_expr: WireExpr::from(21).with_suffix("/z2_pub1"), + }), + }); let primitives2 = Arc::new(ClientPrimitives::new()); let face2 = zwrite!(tables.tables).open_face( @@ -591,7 +572,15 @@ fn client_test() { 31, &"test/client".into(), ); - primitives2.decl_resource(31, &"test/client".into()); + primitives2.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::DeclareKeyExpr(DeclareKeyExpr { + id: 31, + wire_expr: "test/client".into(), + }), + }); declare_client_subscription( &tables, zread!(tables.tables), @@ -603,15 +592,22 @@ fn client_test() { primitives0.clear_data(); primitives1.clear_data(); primitives2.clear_data(); + full_reentrant_route_data( &tables.tables, &face0.upgrade().unwrap(), &"test/client/z1_wr1".into(), - Channel::default(), - CongestionControl::default(), - None, - ZBuf::default(), - None, + ext::QoSType::default(), + PushBody::Put(Put { + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + payload: ZBuf::empty(), + }), + 0, ); // functionnal check @@ -633,11 +629,17 @@ fn client_test() { &tables.tables, &face0.upgrade().unwrap(), &WireExpr::from(11).with_suffix("/z1_wr2"), - Channel::default(), - CongestionControl::default(), - None, - ZBuf::default(), - None, + ext::QoSType::default(), + PushBody::Put(Put { + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + payload: ZBuf::empty(), + }), + 0, ); // functionnal check @@ -659,11 +661,17 @@ fn client_test() { &tables.tables, &face1.upgrade().unwrap(), &"test/client/**".into(), - Channel::default(), - CongestionControl::default(), - None, - ZBuf::default(), - None, + ext::QoSType::default(), + PushBody::Put(Put { + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + payload: ZBuf::empty(), + }), + 0, ); // functionnal check @@ -685,11 +693,17 @@ fn client_test() { &tables.tables, &face0.upgrade().unwrap(), &12.into(), - Channel::default(), - CongestionControl::default(), - None, - ZBuf::default(), - None, + ext::QoSType::default(), + PushBody::Put(Put { + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + payload: ZBuf::empty(), + }), + 0, ); // functionnal check @@ -711,11 +725,17 @@ fn client_test() { &tables.tables, &face1.upgrade().unwrap(), &22.into(), - Channel::default(), - CongestionControl::default(), - None, - ZBuf::default(), - None, + ext::QoSType::default(), + PushBody::Put(Put { + timestamp: None, + encoding: Encoding::default(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + payload: ZBuf::empty(), + }), + 0, ); // functionnal check diff --git a/zenoh/src/prelude.rs b/zenoh/src/prelude.rs index 81abbd774e..380098900a 100644 --- a/zenoh/src/prelude.rs +++ b/zenoh/src/prelude.rs @@ -50,11 +50,12 @@ pub(crate) mod common { pub use zenoh_protocol::core::SampleKind; pub use crate::publication::Priority; - pub use zenoh_protocol::core::{CongestionControl, ConsolidationMode, Reliability}; + pub use zenoh_protocol::core::{CongestionControl, Reliability, WhatAmI}; + /// A [`Locator`] contains a choice of protocol, an address and port, as well as optional additional properties to work with. + pub use zenoh_protocol::core::EndPoint; /// A [`Locator`] contains a choice of protocol, an address and port, as well as optional additional properties to work with. pub use zenoh_protocol::core::Locator; - pub use zenoh_protocol::core::ZInt; /// The global unique id of a zenoh peer. pub use zenoh_protocol::core::ZenohId; } diff --git a/zenoh/src/publication.rs b/zenoh/src/publication.rs index f6f4d51e4b..071eb97da9 100644 --- a/zenoh/src/publication.rs +++ b/zenoh/src/publication.rs @@ -16,13 +16,18 @@ use crate::net::transport::Primitives; use crate::prelude::*; -use crate::subscriber::Reliability; +use crate::sample::DataInfo; use crate::Encoding; use crate::SessionRef; use crate::Undeclarable; use std::future::Ready; use zenoh_core::{zread, AsyncResolve, Resolvable, Resolve, SyncResolve}; -use zenoh_protocol::{core::Channel, zenoh::DataInfo}; +use zenoh_protocol::network::push::ext; +use zenoh_protocol::network::Mapping; +use zenoh_protocol::network::Push; +use zenoh_protocol::zenoh::Del; +use zenoh_protocol::zenoh::PushBody; +use zenoh_protocol::zenoh::Put; use zenoh_result::ZResult; /// The kind of congestion control. @@ -129,41 +134,48 @@ impl SyncResolve for PutBuilder<'_, '_> { .as_ref() .unwrap() .clone(); - - let info = DataInfo { - kind, - encoding: if value.encoding != Encoding::default() { - Some(value.encoding) - } else { - None - }, - timestamp: publisher.session.runtime.new_timestamp(), - ..Default::default() - }; - let data_info = if info != DataInfo::default() { - Some(info) - } else { - None - }; + let timestamp = publisher.session.runtime.new_timestamp(); if publisher.destination != Locality::SessionLocal { - primitives.send_data( - &key_expr.to_wire(&publisher.session), - value.payload.clone(), - Channel { - priority: publisher.priority.into(), - reliability: Reliability::Reliable, // @TODO: need to check subscriptions to determine the right reliability value + primitives.send_push(Push { + wire_expr: key_expr.to_wire(&publisher.session).to_owned(), + ext_qos: ext::QoSType::new( + publisher.priority.into(), + publisher.congestion_control, + false, + ), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + payload: match kind { + SampleKind::Put => PushBody::Put(Put { + timestamp, + encoding: value.encoding.clone(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + payload: value.payload.clone(), + }), + SampleKind::Delete => PushBody::Del(Del { + timestamp, + ext_sinfo: None, + ext_unknown: vec![], + }), }, - publisher.congestion_control, - data_info.clone(), - None, - ); + }); } if publisher.destination != Locality::Remote { + let data_info = DataInfo { + kind, + encoding: Some(value.encoding), + timestamp, + ..Default::default() + }; + publisher.session.handle_data( true, &key_expr.to_wire(&publisher.session), - data_info, + Some(data_info), value.payload, ); } @@ -416,40 +428,38 @@ impl SyncResolve for Publication<'_> { .unwrap() .clone(); - let info = DataInfo { - kind, - encoding: if value.encoding != Encoding::default() { - Some(value.encoding) - } else { - None - }, - timestamp: publisher.session.runtime.new_timestamp(), - ..Default::default() - }; - let data_info = if info != DataInfo::default() { - Some(info) - } else { - None - }; - if publisher.destination != Locality::SessionLocal { - primitives.send_data( - &publisher.key_expr.to_wire(&publisher.session), - value.payload.clone(), - Channel { - priority: publisher.priority.into(), - reliability: Reliability::Reliable, // @TODO: need to check subscriptions to determine the right reliability value - }, - publisher.congestion_control, - data_info.clone(), - None, - ); + primitives.send_push(Push { + wire_expr: publisher.key_expr.to_wire(&publisher.session).to_owned(), + ext_qos: ext::QoSType::new( + publisher.priority.into(), + publisher.congestion_control, + false, + ), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + payload: PushBody::Put(Put { + timestamp: publisher.session.runtime.new_timestamp(), + encoding: value.encoding.clone(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + payload: value.payload.clone(), + }), + }); } if publisher.destination != Locality::Remote { + let data_info = DataInfo { + kind, + encoding: Some(value.encoding), + timestamp: publisher.session.runtime.new_timestamp(), + ..Default::default() + }; publisher.session.handle_data( true, &publisher.key_expr.to_wire(&publisher.session), - data_info, + Some(data_info), value.payload, ); } @@ -578,6 +588,7 @@ impl<'a, 'b> SyncResolve for PublisherBuilder<'a, 'b> { KeyExpr(crate::key_expr::KeyExprInner::BorrowedWire { key_expr, expr_id, + mapping: Mapping::Sender, prefix_len, session_id, }) @@ -587,6 +598,7 @@ impl<'a, 'b> SyncResolve for PublisherBuilder<'a, 'b> { KeyExpr(crate::key_expr::KeyExprInner::Wire { key_expr, expr_id, + mapping: Mapping::Sender, prefix_len, session_id, }) diff --git a/zenoh/src/queryable.rs b/zenoh/src/queryable.rs index 603eb95f7a..ed0560d759 100644 --- a/zenoh/src/queryable.rs +++ b/zenoh/src/queryable.rs @@ -27,6 +27,9 @@ use std::ops::Deref; use std::sync::Arc; use zenoh_core::{AsyncResolve, Resolvable, SyncResolve}; use zenoh_protocol::core::WireExpr; +use zenoh_protocol::network::{response, Mapping, RequestId, Response, ResponseFinal}; +use zenoh_protocol::zenoh::reply::ext::ConsolidationType; +use zenoh_protocol::zenoh::{self, ResponseBody}; use zenoh_result::ZResult; use zenoh_transport::Primitives; @@ -38,14 +41,18 @@ pub(crate) struct QueryInner { /// This Query's body. pub(crate) value: Option, - pub(crate) qid: ZInt, + pub(crate) qid: RequestId, pub(crate) zid: ZenohId, pub(crate) primitives: Arc, } impl Drop for QueryInner { fn drop(&mut self) { - self.primitives.send_reply_final(self.qid); + self.primitives.send_response_final(ResponseFinal { + rid: self.qid, + ext_qos: response::ext::QoSType::response_final_default(), + ext_tstamp: None, + }); } } @@ -156,16 +163,39 @@ impl SyncResolve for ReplyBuilder<'_> { bail!("Attempted to reply on `{}`, which does not intersect with query `{}`, despite query only allowing replies on matching key expressions", sample.key_expr, self.query.key_expr()) } let (key_expr, payload, data_info) = sample.split(); - self.query.inner.primitives.send_reply_data( - self.query.inner.qid, - self.query.inner.zid, - WireExpr { + self.query.inner.primitives.send_response(Response { + rid: self.query.inner.qid, + wire_expr: WireExpr { scope: 0, - suffix: std::borrow::Cow::Borrowed(key_expr.as_str()), + suffix: std::borrow::Cow::Owned(key_expr.into()), + mapping: Mapping::Sender, }, - Some(data_info), - payload, - ); + payload: ResponseBody::Reply(zenoh::Reply { + timestamp: data_info.timestamp, + encoding: data_info.encoding.unwrap_or_default(), + ext_sinfo: if data_info.source_id.is_some() || data_info.source_sn.is_some() + { + Some(zenoh::reply::ext::SourceInfoType { + zid: data_info.source_id.unwrap_or_default(), + eid: 0, // TODO + sn: data_info.source_sn.unwrap_or_default() as u32, + }) + } else { + None + }, + ext_consolidation: ConsolidationType::default(), + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + payload, + }), + ext_qos: response::ext::QoSType::response_default(), + ext_tstamp: None, + ext_respid: Some(response::ext::ResponderIdType { + zid: self.query.inner.zid, + eid: 0, // TODO + }), + }); Ok(()) } Err(_) => Err(zerror!("Replying errors is not yet supported!").into()), diff --git a/zenoh/src/sample.rs b/zenoh/src/sample.rs index 058a735b65..6d12d5a819 100644 --- a/zenoh/src/sample.rs +++ b/zenoh/src/sample.rs @@ -22,9 +22,9 @@ use crate::time::{new_reception_timestamp, Timestamp}; #[zenoh_macros::unstable] use serde::Serialize; use std::convert::{TryFrom, TryInto}; -#[zenoh_macros::unstable] -use zenoh_protocol::core::ZInt; -use zenoh_protocol::zenoh::DataInfo; +use zenoh_protocol::core::Encoding; + +pub type SourceSn = u64; /// The locality of samples to be received by subscribers or targeted by publishers. #[zenoh_macros::unstable] @@ -44,6 +44,15 @@ pub(crate) enum Locality { Any, } +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub(crate) struct DataInfo { + pub kind: SampleKind, + pub encoding: Option, + pub timestamp: Option, + pub source_id: Option, + pub source_sn: Option, +} + /// Informations on the source of a zenoh [`Sample`]. #[zenoh_macros::unstable] #[derive(Debug, Clone)] @@ -51,7 +60,7 @@ pub struct SourceInfo { /// The [`ZenohId`] of the zenoh instance that published the concerned [`Sample`]. pub source_id: Option, /// The sequence number of the [`Sample`] from the source. - pub source_sn: Option, + pub source_sn: Option, } #[test] @@ -190,8 +199,6 @@ impl Sample { kind: self.kind, encoding: Some(self.value.encoding), timestamp: self.timestamp, - #[cfg(feature = "shared-memory")] - sliced: false, #[cfg(feature = "unstable")] source_id: self.source_info.source_id, #[cfg(not(feature = "unstable"))] diff --git a/zenoh/src/scouting.rs b/zenoh/src/scouting.rs index b9e1a9e396..5b85a38359 100644 --- a/zenoh/src/scouting.rs +++ b/zenoh/src/scouting.rs @@ -16,12 +16,9 @@ use crate::net::runtime::{orchestrator::Loop, Runtime}; use async_std::net::UdpSocket; use futures::StreamExt; -use std::future::Ready; -use std::{fmt, ops::Deref}; -use zenoh_config::{ - whatami::WhatAmIMatcher, ZN_MULTICAST_INTERFACE_DEFAULT, ZN_MULTICAST_IPV4_ADDRESS_DEFAULT, -}; +use std::{fmt, future::Ready, net::SocketAddr, ops::Deref}; use zenoh_core::{AsyncResolve, Resolvable, SyncResolve}; +use zenoh_protocol::core::WhatAmIMatcher; use zenoh_result::ZResult; /// Constants and helpers for zenoh `whatami` flags. @@ -295,23 +292,12 @@ fn scout( callback: Callback<'static, Hello>, ) -> ZResult { log::trace!("scout({}, {})", what, &config); - let default_addr = match ZN_MULTICAST_IPV4_ADDRESS_DEFAULT.parse() { - Ok(addr) => addr, - Err(e) => { - bail!( - "invalid default addr {}: {:?}", - ZN_MULTICAST_IPV4_ADDRESS_DEFAULT, - &e - ) - } - }; + let default_addr = SocketAddr::from(zenoh_config::defaults::scouting::multicast::address); let addr = config.scouting.multicast.address().unwrap_or(default_addr); - let ifaces = config - .scouting - .multicast - .interface() - .as_ref() - .map_or(ZN_MULTICAST_INTERFACE_DEFAULT, |s| s.as_ref()); + let ifaces = config.scouting.multicast.interface().as_ref().map_or( + zenoh_config::defaults::scouting::multicast::interface, + |s| s.as_ref(), + ); let (stop_sender, stop_receiver) = flume::bounded::<()>(1); let ifaces = Runtime::get_interfaces(ifaces); if !ifaces.is_empty() { diff --git a/zenoh/src/session.rs b/zenoh/src/session.rs index f96afdd0c4..744f21965f 100644 --- a/zenoh/src/session.rs +++ b/zenoh/src/session.rs @@ -28,6 +28,7 @@ use crate::prelude::{KeyExpr, Parameters}; use crate::publication::*; use crate::query::*; use crate::queryable::*; +use crate::sample::DataInfo; use crate::selector::TIME_RANGE_KEY; use crate::subscriber::*; use crate::Id; @@ -43,7 +44,7 @@ use std::convert::TryFrom; use std::convert::TryInto; use std::fmt; use std::ops::Deref; -use std::sync::atomic::{AtomicU16, AtomicU64, AtomicUsize, Ordering}; +use std::sync::atomic::{AtomicU16, AtomicUsize, Ordering}; use std::sync::Arc; use std::sync::RwLock; use std::time::Duration; @@ -52,19 +53,34 @@ use zenoh_buffers::ZBuf; use zenoh_collections::SingleOrVec; use zenoh_config::unwrap_or_default; use zenoh_core::{zconfigurable, zread, Resolve, ResolveClosure, ResolveFuture, SyncResolve}; +use zenoh_protocol::network::AtomicRequestId; +use zenoh_protocol::network::RequestId; use zenoh_protocol::{ core::{ key_expr::{keyexpr, OwnedKeyExpr}, - Channel, CongestionControl, ExprId, QueryTarget, QueryableInfo, SubInfo, WireExpr, ZInt, - ZenohId, EMPTY_EXPR_ID, + AtomicExprId, CongestionControl, ExprId, WireExpr, ZenohId, EMPTY_EXPR_ID, + }, + network::{ + declare::{ + self, common::ext::WireExprType, queryable::ext::QueryableInfo, + subscriber::ext::SubscriberInfo, Declare, DeclareBody, DeclareKeyExpr, + DeclareQueryable, DeclareSubscriber, UndeclareQueryable, UndeclareSubscriber, + }, + ext, + request::{self, ext::TargetType, Request}, + Mapping, Push, Response, ResponseFinal, + }, + zenoh::{ + query::{ + self, + ext::{ConsolidationType, QueryBodyType}, + }, + Pull, PushBody, RequestBody, ResponseBody, }, - zenoh::{DataInfo, QueryBody, RoutingContext}, }; use zenoh_result::ZResult; use zenoh_util::core::AsyncResolve; -pub type AtomicZInt = AtomicU64; - zconfigurable! { pub(crate) static ref API_DATA_RECEPTION_CHANNEL_SIZE: usize = 256; pub(crate) static ref API_QUERY_RECEPTION_CHANNEL_SIZE: usize = 256; @@ -75,41 +91,41 @@ zconfigurable! { pub(crate) struct SessionState { pub(crate) primitives: Option>, // @TODO replace with MaybeUninit ?? - pub(crate) expr_id_counter: AtomicUsize, // @TODO: manage rollover and uniqueness - pub(crate) qid_counter: AtomicZInt, + pub(crate) expr_id_counter: AtomicExprId, // @TODO: manage rollover and uniqueness + pub(crate) qid_counter: AtomicRequestId, pub(crate) decl_id_counter: AtomicUsize, pub(crate) local_resources: HashMap, pub(crate) remote_resources: HashMap, - pub(crate) publications: Vec, + //pub(crate) publications: Vec, pub(crate) subscribers: HashMap>, pub(crate) queryables: HashMap>, #[cfg(feature = "unstable")] pub(crate) tokens: HashMap>, - pub(crate) queries: HashMap, + pub(crate) queries: HashMap, pub(crate) aggregated_subscribers: Vec, - pub(crate) aggregated_publishers: Vec, + //pub(crate) aggregated_publishers: Vec, } impl SessionState { pub(crate) fn new( aggregated_subscribers: Vec, - aggregated_publishers: Vec, + _aggregated_publishers: Vec, ) -> SessionState { SessionState { primitives: None, - expr_id_counter: AtomicUsize::new(1), // Note: start at 1 because 0 is reserved for NO_RESOURCE - qid_counter: AtomicZInt::new(0), + expr_id_counter: AtomicExprId::new(1), // Note: start at 1 because 0 is reserved for NO_RESOURCE + qid_counter: AtomicRequestId::new(0), decl_id_counter: AtomicUsize::new(0), local_resources: HashMap::new(), remote_resources: HashMap::new(), - publications: Vec::new(), + //publications: Vec::new(), subscribers: HashMap::new(), queryables: HashMap::new(), #[cfg(feature = "unstable")] tokens: HashMap::new(), queries: HashMap::new(), aggregated_subscribers, - aggregated_publishers, + //aggregated_publishers, } } } @@ -121,19 +137,19 @@ impl SessionState { } #[inline] - fn get_remote_res(&self, id: &ExprId) -> Option<&Resource> { - match self.remote_resources.get(id) { - None => self.local_resources.get(id), - res => res, + fn get_remote_res(&self, id: &ExprId, mapping: Mapping) -> Option<&Resource> { + match mapping { + Mapping::Receiver => self.local_resources.get(id), + Mapping::Sender => self.remote_resources.get(id), } } #[inline] - fn get_res(&self, id: &ExprId, local: bool) -> Option<&Resource> { + fn get_res(&self, id: &ExprId, mapping: Mapping, local: bool) -> Option<&Resource> { if local { self.get_local_res(id) } else { - self.get_remote_res(id) + self.get_remote_res(id, mapping) } } @@ -141,7 +157,7 @@ impl SessionState { if key_expr.scope == EMPTY_EXPR_ID { Ok(unsafe { keyexpr::from_str_unchecked(key_expr.suffix.as_ref()) }.into()) } else if key_expr.suffix.is_empty() { - match self.get_remote_res(&key_expr.scope) { + match self.get_remote_res(&key_expr.scope, key_expr.mapping) { Some(Resource::Node(ResourceNode { key_expr, .. })) => Ok(key_expr.into()), Some(Resource::Prefix { prefix }) => bail!( "Received {:?}, where {} is `{}`, which isn't a valid key expression", @@ -153,7 +169,7 @@ impl SessionState { } } else { [ - match self.get_remote_res(&key_expr.scope) { + match self.get_remote_res(&key_expr.scope, key_expr.mapping) { Some(Resource::Node(ResourceNode { key_expr, .. })) => key_expr.as_str(), Some(Resource::Prefix { prefix }) => prefix.as_ref(), None => bail!("Remote resource {} not found", key_expr.scope), @@ -646,6 +662,7 @@ impl Session { KeyExpr(KeyExprInner::BorrowedWire { key_expr, expr_id, + mapping: Mapping::Sender, prefix_len, session_id: sid, }) @@ -654,6 +671,7 @@ impl Session { KeyExpr(KeyExprInner::Wire { key_expr, expr_id, + mapping: Mapping::Sender, prefix_len, session_id: sid, }) @@ -839,7 +857,10 @@ impl Session { }) } - pub(crate) fn declare_prefix<'a>(&'a self, prefix: &'a str) -> impl Resolve + Send + 'a { + pub(crate) fn declare_prefix<'a>( + &'a self, + prefix: &'a str, + ) -> impl Resolve + Send + 'a { ResolveClosure::new(move || { trace!("declare_prefix({:?})", prefix); let mut state = zwrite!(self.state); @@ -850,7 +871,7 @@ impl Session { { Some((expr_id, _res)) => *expr_id, None => { - let expr_id = state.expr_id_counter.fetch_add(1, Ordering::SeqCst) as ZInt; + let expr_id = state.expr_id_counter.fetch_add(1, Ordering::SeqCst); let mut res = Resource::new(Box::from(prefix)); if let Resource::Node(ResourceNode { key_expr, @@ -867,13 +888,19 @@ impl Session { state.local_resources.insert(expr_id, res); let primitives = state.primitives.as_ref().unwrap().clone(); drop(state); - primitives.decl_resource( - expr_id, - &WireExpr { - scope: 0, - suffix: std::borrow::Cow::Borrowed(prefix), - }, - ); + primitives.send_declare(Declare { + ext_qos: declare::ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: declare::ext::NodeIdType::default(), + body: DeclareBody::DeclareKeyExpr(DeclareKeyExpr { + id: expr_id, + wire_expr: WireExpr { + scope: 0, + suffix: prefix.to_owned().into(), + mapping: Mapping::Sender, + }, + }), + }); expr_id } } @@ -890,30 +917,30 @@ impl Session { /// * `key_expr` - The key expression to publish pub(crate) fn declare_publication_intent<'a>( &'a self, - key_expr: KeyExpr<'a>, + _key_expr: KeyExpr<'a>, ) -> impl Resolve> + Send + 'a { ResolveClosure::new(move || { - log::trace!("declare_publication({:?})", key_expr); - let mut state = zwrite!(self.state); - if !state.publications.iter().any(|p| **p == **key_expr) { - let declared_pub = if let Some(join_pub) = state - .aggregated_publishers - .iter() - .find(|s| s.includes(&key_expr)) - { - let joined_pub = state.publications.iter().any(|p| join_pub.includes(p)); - (!joined_pub).then(|| join_pub.clone().into()) - } else { - Some(key_expr.clone()) - }; - state.publications.push(key_expr.into()); - - if let Some(res) = declared_pub { - let primitives = state.primitives.as_ref().unwrap().clone(); - drop(state); - primitives.decl_publisher(&res.to_wire(self), None); - } - } + // log::trace!("declare_publication({:?})", key_expr); + // let mut state = zwrite!(self.state); + // if !state.publications.iter().any(|p| **p == **key_expr) { + // let declared_pub = if let Some(join_pub) = state + // .aggregated_publishers + // .iter() + // .find(|s| s.includes(&key_expr)) + // { + // let joined_pub = state.publications.iter().any(|p| join_pub.includes(p)); + // (!joined_pub).then(|| join_pub.clone().into()) + // } else { + // Some(key_expr.clone()) + // }; + // state.publications.push(key_expr.into()); + + // if let Some(res) = declared_pub { + // let primitives = state.primitives.as_ref().unwrap().clone(); + // drop(state); + // primitives.decl_publisher(&res.to_wire(self), None); + // } + // } Ok(()) }) } @@ -926,36 +953,36 @@ impl Session { /// * `key_expr` - The key expression of the publication to undeclarte pub(crate) fn undeclare_publication_intent<'a>( &'a self, - key_expr: KeyExpr<'a>, + _key_expr: KeyExpr<'a>, ) -> impl Resolve> + 'a { ResolveClosure::new(move || { - let mut state = zwrite!(self.state); - if let Some(idx) = state.publications.iter().position(|p| **p == *key_expr) { - trace!("undeclare_publication({:?})", key_expr); - state.publications.remove(idx); - match state - .aggregated_publishers - .iter() - .find(|s| s.includes(&key_expr)) - { - Some(join_pub) => { - let joined_pub = state.publications.iter().any(|p| join_pub.includes(p)); - if !joined_pub { - let primitives = state.primitives.as_ref().unwrap().clone(); - let key_expr = WireExpr::from(join_pub).to_owned(); - drop(state); - primitives.forget_publisher(&key_expr, None); - } - } - None => { - let primitives = state.primitives.as_ref().unwrap().clone(); - drop(state); - primitives.forget_publisher(&key_expr.to_wire(self), None); - } - }; - } else { - bail!("Unable to find publication") - } + // let mut state = zwrite!(self.state); + // if let Some(idx) = state.publications.iter().position(|p| **p == *key_expr) { + // trace!("undeclare_publication({:?})", key_expr); + // state.publications.remove(idx); + // match state + // .aggregated_publishers + // .iter() + // .find(|s| s.includes(&key_expr)) + // { + // Some(join_pub) => { + // let joined_pub = state.publications.iter().any(|p| join_pub.includes(p)); + // if !joined_pub { + // let primitives = state.primitives.as_ref().unwrap().clone(); + // let key_expr = WireExpr::from(join_pub).to_owned(); + // drop(state); + // primitives.forget_publisher(&key_expr, None); + // } + // } + // None => { + // let primitives = state.primitives.as_ref().unwrap().clone(); + // drop(state); + // primitives.forget_publisher(&key_expr.to_wire(self), None); + // } + // }; + // } else { + // bail!("Unable to find publication") + // } Ok(()) }) } @@ -966,7 +993,7 @@ impl Session { scope: &Option, origin: Locality, callback: Callback<'static, Sample>, - info: &SubInfo, + info: &SubscriberInfo, ) -> ZResult> { let mut state = zwrite!(self.state); log::trace!("subscribe({:?})", key_expr); @@ -1062,7 +1089,16 @@ impl Session { // key_expr.to_wire(self) // }; - primitives.decl_subscriber(&key_expr.to_wire(self), info, None); + primitives.send_declare(Declare { + ext_qos: declare::ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: declare::ext::NodeIdType::default(), + body: DeclareBody::DeclareSubscriber(DeclareSubscriber { + id: id as u32, + wire_expr: key_expr.to_wire(self).to_owned(), + ext_info: *info, + }), + }); } Ok(sub_state) @@ -1110,9 +1146,17 @@ impl Session { }); if !joined_sub { let primitives = state.primitives.as_ref().unwrap().clone(); - let key_expr = WireExpr::from(join_sub).to_owned(); + let wire_expr = WireExpr::from(join_sub).to_owned(); drop(state); - primitives.forget_subscriber(&key_expr, None); + primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::UndeclareSubscriber(UndeclareSubscriber { + id: 0, // TODO + ext_wire_expr: WireExprType { wire_expr }, + }), + }); } } None => { @@ -1123,7 +1167,17 @@ impl Session { if !twin_sub { let primitives = state.primitives.as_ref().unwrap().clone(); drop(state); - primitives.forget_subscriber(&key_expr.to_wire(self), None); + primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::UndeclareSubscriber(UndeclareSubscriber { + id: 0, // TODO + ext_wire_expr: WireExprType { + wire_expr: key_expr.to_wire(self).to_owned(), + }, + }), + }); } } }; @@ -1163,7 +1217,16 @@ impl Session { complete, distance: 0, }; - primitives.decl_queryable(key_expr, &qabl_info, None); + primitives.send_declare(Declare { + ext_qos: declare::ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: declare::ext::NodeIdType::default(), + body: DeclareBody::DeclareQueryable(DeclareQueryable { + id: id as u32, + wire_expr: key_expr.to_owned(), + ext_info: qabl_info, + }), + }); } } #[cfg(not(feature = "complete_n"))] @@ -1176,13 +1239,22 @@ impl Session { if origin != Locality::SessionLocal && (!twin_qabl || (!complete_twin_qabl && complete)) { let primitives = state.primitives.as_ref().unwrap().clone(); - let complete = ZInt::from(!complete_twin_qabl && complete); + let complete = u8::from(!complete_twin_qabl && complete); drop(state); let qabl_info = QueryableInfo { complete, distance: 0, }; - primitives.decl_queryable(key_expr, &qabl_info, None); + primitives.send_declare(Declare { + ext_qos: declare::ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: declare::ext::NodeIdType::default(), + body: DeclareBody::DeclareQueryable(DeclareQueryable { + id: id as u32, + wire_expr: key_expr.to_owned(), + ext_info: qabl_info, + }), + }); } } Ok(qable_state) @@ -1207,7 +1279,7 @@ impl Session { } #[cfg(feature = "complete_n")] - pub(crate) fn complete_twin_qabls(state: &SessionState, key: &WireExpr) -> ZInt { + pub(crate) fn complete_twin_qabls(state: &SessionState, key: &WireExpr) -> u8 { state .queryables .values() @@ -1217,7 +1289,7 @@ impl Session { && state.local_wireexpr_to_expr(&q.key_expr).unwrap() == state.local_wireexpr_to_expr(key).unwrap() }) - .count() as ZInt + .count() as u8 } pub(crate) fn close_queryable(&self, qid: usize) -> ZResult<()> { @@ -1238,7 +1310,16 @@ impl Session { complete, distance: 0, }; - primitives.decl_queryable(&qable_state.key_expr, &qabl_info, None); + primitives.send_declare(Declare { + ext_qos: declare::ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: declare::ext::NodeIdType::default(), + body: DeclareBody::DeclareQueryable(DeclareQueryable { + id: 0, // TODO + wire_expr: qable_state.key_expr.clone(), + ext_info: qabl_info, + }), + }); } #[cfg(not(feature = "complete_n"))] { @@ -1248,14 +1329,33 @@ impl Session { complete: 0, distance: 0, }; - primitives.decl_queryable(&qable_state.key_expr, &qabl_info, None); + primitives.send_declare(Declare { + ext_qos: declare::ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: declare::ext::NodeIdType::default(), + body: DeclareBody::DeclareQueryable(DeclareQueryable { + id: 0, // TODO + wire_expr: qable_state.key_expr.clone(), + ext_info: qabl_info, + }), + }); } } } } else { // There are no more Queryables on the same KeyExpr. drop(state); - primitives.forget_queryable(&qable_state.key_expr, None); + primitives.send_declare(Declare { + ext_qos: declare::ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: declare::ext::NodeIdType::default(), + body: DeclareBody::UndeclareQueryable(UndeclareQueryable { + id: 0, // TODO + ext_wire_expr: WireExprType { + wire_expr: qable_state.key_expr.clone(), + }, + }), + }); } } Ok(()) @@ -1281,8 +1381,16 @@ impl Session { state.tokens.insert(tok_state.id, tok_state.clone()); let primitives = state.primitives.as_ref().unwrap().clone(); drop(state); - let sub_info = SubInfo::default(); - primitives.decl_subscriber(&key_expr.to_wire(self), &sub_info, None); + primitives.send_declare(Declare { + ext_qos: declare::ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: declare::ext::NodeIdType::default(), + body: DeclareBody::DeclareSubscriber(DeclareSubscriber { + id: id as u32, + wire_expr: key_expr.to_wire(self).to_owned(), + ext_info: SubscriberInfo::default(), + }), + }); Ok(tok_state) } @@ -1297,7 +1405,17 @@ impl Session { if !twin_tok { let primitives = state.primitives.as_ref().unwrap().clone(); drop(state); - primitives.forget_subscriber(&key_expr.to_wire(self), None); + primitives.send_declare(Declare { + ext_qos: ext::QoSType::declare_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + body: DeclareBody::UndeclareSubscriber(UndeclareSubscriber { + id: 0, // TODO + ext_wire_expr: WireExprType { + wire_expr: key_expr.to_wire(self).to_owned(), + }, + }), + }); } Ok(()) } else { @@ -1315,7 +1433,7 @@ impl Session { let mut callbacks = SingleOrVec::default(); let state = zread!(self.state); if key_expr.suffix.is_empty() { - match state.get_res(&key_expr.scope, local) { + match state.get_res(&key_expr.scope, key_expr.mapping, local) { Some(Resource::Node(res)) => { for sub in &res.subscribers { if sub.origin == Locality::Any @@ -1424,7 +1542,19 @@ impl Session { let state = zread!(self.state); let primitives = state.primitives.as_ref().unwrap().clone(); drop(state); - primitives.send_pull(true, &key_expr.to_wire(self), 0, &None); + primitives.send_request(Request { + id: 0, // TODO + wire_expr: key_expr.to_wire(self).to_owned(), + ext_qos: ext::QoSType::request_default(), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::default(), + ext_target: request::ext::TargetType::default(), + ext_budget: None, + ext_timeout: None, + payload: RequestBody::Pull(Pull { + ext_unknown: vec![], + }), + }); Ok(()) }) } @@ -1489,7 +1619,7 @@ impl Session { }; log::trace!("Register query {} (nb_final = {})", qid, nb_final); - let wexpr = selector.key_expr.to_wire(self); + let wexpr = selector.key_expr.to_wire(self).to_owned(); state.queries.insert( qid, QueryState { @@ -1506,24 +1636,28 @@ impl Session { drop(state); if destination != Locality::SessionLocal { - primitives.send_query( - &wexpr, - selector.parameters(), - qid, - target, - consolidation, - value.as_ref().map(|v| { - let data_info = DataInfo { - encoding: Some(v.encoding.clone()), - ..Default::default() - }; - QueryBody { - data_info, + primitives.send_request(Request { + id: qid, + wire_expr: wexpr.clone(), + ext_qos: request::ext::QoSType::request_default(), + ext_tstamp: None, + ext_nodeid: request::ext::NodeIdType::default(), + ext_target: target, + ext_budget: None, + ext_timeout: Some(timeout), + payload: RequestBody::Query(zenoh_protocol::zenoh::Query { + parameters: selector.parameters().to_string(), + ext_sinfo: None, + ext_consolidation: consolidation.into(), + ext_body: value.as_ref().map(|v| query::ext::QueryBodyType { + #[cfg(feature = "shared-memory")] + ext_shm: None, + encoding: v.encoding.clone(), payload: v.payload.clone(), - } + }), + ext_unknown: vec![], }), - None, - ); + }); } if destination != Locality::Remote { self.handle_query( @@ -1532,16 +1666,12 @@ impl Session { selector.parameters(), qid, target, - consolidation, - value.map(|v| { - let data_info = DataInfo { - encoding: Some(v.encoding), - ..Default::default() - }; - QueryBody { - data_info, - payload: v.payload, - } + consolidation.into(), + value.as_ref().map(|v| query::ext::QueryBodyType { + #[cfg(feature = "shared-memory")] + ext_shm: None, + encoding: v.encoding.clone(), + payload: v.payload.clone(), }), ); } @@ -1554,10 +1684,10 @@ impl Session { local: bool, key_expr: &WireExpr, parameters: &str, - qid: ZInt, - _target: QueryTarget, - _consolidation: ConsolidationMode, - body: Option, + qid: RequestId, + _target: TargetType, + _consolidation: ConsolidationType, + body: Option, ) { let (primitives, key_expr, callbacks) = { let state = zread!(self.state); @@ -1609,7 +1739,7 @@ impl Session { parameters, value: body.map(|b| Value { payload: b.payload, - encoding: b.data_info.encoding.unwrap_or_default(), + encoding: b.encoding, }), qid, zid, @@ -1773,333 +1903,319 @@ impl SessionDeclarations for Arc { } impl Primitives for Session { - fn decl_resource(&self, expr_id: ZInt, wire_expr: &WireExpr) { - trace!("recv Decl Resource {} {:?}", expr_id, wire_expr); - let state = &mut zwrite!(self.state); - match state.remote_key_to_expr(wire_expr) { - Ok(key_expr) => { - let mut subs = Vec::new(); - for sub in state.subscribers.values() { - if key_expr.intersects(&sub.key_expr) { - subs.push(sub.clone()); + fn send_declare(&self, msg: zenoh_protocol::network::Declare) { + match msg.body { + zenoh_protocol::network::DeclareBody::DeclareKeyExpr(m) => { + trace!("recv DeclareKeyExpr {} {:?}", m.id, m.wire_expr); + let state = &mut zwrite!(self.state); + match state.remote_key_to_expr(&m.wire_expr) { + Ok(key_expr) => { + let mut subs = Vec::new(); + for sub in state.subscribers.values() { + if key_expr.intersects(&sub.key_expr) { + subs.push(sub.clone()); + } + } + let res = Resource::Node(ResourceNode { + key_expr: key_expr.into(), + subscribers: subs, + }); + + state.remote_resources.insert(m.id, res); } + Err(e) => error!( + "Received Resource for invalid wire_expr `{}`: {}", + m.wire_expr, e + ), } - let res = Resource::Node(ResourceNode { - key_expr: key_expr.into(), - subscribers: subs, - }); - - state.remote_resources.insert(expr_id, res); } - Err(e) => error!( - "Received Resource for invalid wire_expr `{}`: {}", - wire_expr, e - ), - } - } - - fn forget_resource(&self, _expr_id: ZInt) { - trace!("recv Forget Resource {}", _expr_id); - } - - fn decl_publisher(&self, _key_expr: &WireExpr, _routing_context: Option) { - trace!("recv Decl Publisher {:?}", _key_expr); - } - - fn forget_publisher(&self, _key_expr: &WireExpr, _routing_context: Option) { - trace!("recv Forget Publisher {:?}", _key_expr); - } - - fn decl_subscriber( - &self, - key_expr: &WireExpr, - _sub_info: &SubInfo, - _routing_context: Option, - ) { - trace!("recv Decl Subscriber {:?} , {:?}", key_expr, _sub_info); - #[cfg(feature = "unstable")] - { - let state = zread!(self.state); - match state.wireexpr_to_keyexpr(key_expr, false) { - Ok(expr) => { - if expr - .as_str() - .starts_with(crate::liveliness::PREFIX_LIVELINESS) - { - drop(state); - self.handle_data(false, key_expr, None, ZBuf::default()); + zenoh_protocol::network::DeclareBody::UndeclareKeyExpr(m) => { + trace!("recv UndeclareKeyExpr {}", m.id); + } + zenoh_protocol::network::DeclareBody::DeclareSubscriber(m) => { + trace!("recv DeclareSubscriber {} {:?}", m.id, m.wire_expr); + #[cfg(feature = "unstable")] + { + let state = zread!(self.state); + match state.wireexpr_to_keyexpr(&m.wire_expr, false) { + Ok(expr) => { + if expr + .as_str() + .starts_with(crate::liveliness::PREFIX_LIVELINESS) + { + drop(state); + self.handle_data(false, &m.wire_expr, None, ZBuf::default()); + } + } + Err(err) => { + log::error!("Received DeclareSubscriber for unkown wire_expr: {}", err) + } } } - Err(err) => log::error!("Received Forget Subscriber for unkown key_expr: {}", err), } - } - } - - fn forget_subscriber(&self, key_expr: &WireExpr, _routing_context: Option) { - trace!("recv Forget Subscriber {:?}", key_expr); - #[cfg(feature = "unstable")] - { - let state = zread!(self.state); - match state.wireexpr_to_keyexpr(key_expr, false) { - Ok(expr) => { - if expr - .as_str() - .starts_with(crate::liveliness::PREFIX_LIVELINESS) - { - drop(state); - let data_info = DataInfo { - kind: SampleKind::Delete, - ..Default::default() - }; - self.handle_data(false, key_expr, Some(data_info), ZBuf::default()); + zenoh_protocol::network::DeclareBody::UndeclareSubscriber(m) => { + trace!("recv UndeclareSubscriber {:?}", m.id); + #[cfg(feature = "unstable")] + { + let state = zread!(self.state); + match state.wireexpr_to_keyexpr(&m.ext_wire_expr.wire_expr, false) { + Ok(expr) => { + if expr + .as_str() + .starts_with(crate::liveliness::PREFIX_LIVELINESS) + { + drop(state); + let data_info = DataInfo { + kind: SampleKind::Delete, + ..Default::default() + }; + self.handle_data( + false, + &m.ext_wire_expr.wire_expr, + Some(data_info), + ZBuf::default(), + ); + } + } + Err(err) => { + log::error!("Received Forget Subscriber for unkown key_expr: {}", err) + } } } - Err(err) => log::error!("Received Forget Subscriber for unkown key_expr: {}", err), } + zenoh_protocol::network::DeclareBody::DeclareQueryable(m) => { + trace!("recv DeclareQueryable {} {:?}", m.id, m.wire_expr); + } + zenoh_protocol::network::DeclareBody::UndeclareQueryable(m) => { + trace!("recv UndeclareQueryable {:?}", m.id); + } + DeclareBody::DeclareToken(_) => todo!(), + DeclareBody::UndeclareToken(_) => todo!(), + DeclareBody::DeclareInterest(_) => todo!(), + DeclareBody::FinalInterest(_) => todo!(), + DeclareBody::UndeclareInterest(_) => todo!(), } } - fn decl_queryable( - &self, - _key_expr: &WireExpr, - _qabl_info: &QueryableInfo, - _routing_context: Option, - ) { - trace!("recv Decl Queryable {:?}", _key_expr); - } - - fn forget_queryable(&self, _key_expr: &WireExpr, _routing_context: Option) { - trace!("recv Forget Queryable {:?}", _key_expr); - } - - fn send_data( - &self, - key_expr: &WireExpr, - payload: ZBuf, - channel: Channel, - congestion_control: CongestionControl, - info: Option, - _routing_context: Option, - ) { - trace!( - "recv Data {:?} {:?} {:?} {:?} {:?}", - key_expr, - payload, - channel, - congestion_control, - info, - ); - self.handle_data(false, key_expr, info, payload) + fn send_push(&self, msg: Push) { + trace!("recv Push {:?}", msg); + match msg.payload { + PushBody::Put(m) => { + let info = DataInfo { + kind: SampleKind::Put, + encoding: Some(m.encoding), + timestamp: m.timestamp, + source_id: m.ext_sinfo.as_ref().map(|i| i.zid), + source_sn: m.ext_sinfo.as_ref().map(|i| i.sn as u64), + }; + self.handle_data(false, &msg.wire_expr, Some(info), m.payload) + } + PushBody::Del(m) => { + let info = DataInfo { + kind: SampleKind::Delete, + encoding: None, + timestamp: m.timestamp, + source_id: m.ext_sinfo.as_ref().map(|i| i.zid), + source_sn: m.ext_sinfo.as_ref().map(|i| i.sn as u64), + }; + self.handle_data(false, &msg.wire_expr, Some(info), ZBuf::empty()) + } + } } - fn send_query( - &self, - key_expr: &WireExpr, - parameters: &str, - qid: ZInt, - target: QueryTarget, - consolidation: ConsolidationMode, - body: Option, - _routing_context: Option, - ) { - trace!( - "recv Query {:?} {:?} {:?} {:?}", - key_expr, - parameters, - target, - consolidation - ); - self.handle_query( - false, - key_expr, - parameters, - qid, - target, - consolidation, - body, - ) + fn send_request(&self, msg: Request) { + trace!("recv Request {:?}", msg); + match msg.payload { + RequestBody::Query(m) => self.handle_query( + false, + &msg.wire_expr, + &m.parameters, + msg.id, + msg.ext_target, + m.ext_consolidation, + m.ext_body, + ), + RequestBody::Put(_) => (), + RequestBody::Del(_) => (), + RequestBody::Pull(_) => todo!(), + } } - fn send_reply_data( - &self, - qid: ZInt, - replier_id: ZenohId, - key_expr: WireExpr, - data_info: Option, - payload: ZBuf, - ) { - trace!( - "recv ReplyData {:?} {:?} {:?} {:?} {:?}", - qid, - replier_id, - key_expr, - data_info, - payload - ); - let mut state = zwrite!(self.state); - let key_expr = match state.remote_key_to_expr(&key_expr) { - Ok(key) => key.into_owned(), - Err(e) => { - error!("Received ReplyData for unkown key_expr: {}", e); - return; - } - }; - match state.queries.get_mut(&qid) { - Some(query) => { - if !matches!( - query - .selector - .parameters() - .get_bools([crate::query::_REPLY_KEY_EXPR_ANY_SEL_PARAM]), - Ok([true]) - ) && !query.selector.key_expr.intersects(&key_expr) - { - log::warn!( - "Received ReplyData for `{}` from `{:?}, which didn't match query `{}`: dropping ReplyData.", - key_expr, - replier_id, - query.selector - ); + fn send_response(&self, msg: Response) { + trace!("recv Response {:?}", msg); + if let ResponseBody::Reply(m) = msg.payload { + let mut state = zwrite!(self.state); + let key_expr = match state.remote_key_to_expr(&msg.wire_expr) { + Ok(key) => key.into_owned(), + Err(e) => { + error!("Received ReplyData for unkown key_expr: {}", e); return; } - let key_expr = match &query.scope { - Some(scope) => { - if !key_expr.starts_with(&***scope) { - log::warn!( - "Received ReplyData for `{}` from `{:?}, which didn't start with scope `{}`: dropping ReplyData.", - key_expr, - replier_id, - scope, - ); - return; - } - match KeyExpr::try_from(&key_expr[(scope.len() + 1)..]) { - Ok(key_expr) => key_expr, - Err(e) => { + }; + match state.queries.get_mut(&msg.rid) { + Some(query) => { + if !matches!( + query + .selector + .parameters() + .get_bools([crate::query::_REPLY_KEY_EXPR_ANY_SEL_PARAM]), + Ok([true]) + ) && !query.selector.key_expr.intersects(&key_expr) + { + log::warn!( + "Received Reply for `{}` from `{:?}, which didn't match query `{}`: dropping Reply.", + key_expr, + msg.ext_respid, + query.selector + ); + return; + } + let key_expr = match &query.scope { + Some(scope) => { + if !key_expr.starts_with(&***scope) { log::warn!( - "Error unscoping received ReplyData for `{}` from `{:?}: {}", + "Received Reply for `{}` from `{:?}, which didn't start with scope `{}`: dropping Reply.", key_expr, - replier_id, - e, + msg.ext_respid, + scope, ); return; } + match KeyExpr::try_from(&key_expr[(scope.len() + 1)..]) { + Ok(key_expr) => key_expr, + Err(e) => { + log::warn!( + "Error unscoping received Reply for `{}` from `{:?}: {}", + key_expr, + msg.ext_respid, + e, + ); + return; + } + } } - } - None => key_expr, - }; - let new_reply = Reply { - sample: Ok(Sample::with_info(key_expr.into_owned(), payload, data_info)), - replier_id, - }; - let callback = match query.reception_mode { - ConsolidationMode::None => Some((query.callback.clone(), new_reply)), - ConsolidationMode::Monotonic => { - match query - .replies - .as_ref() - .unwrap() - .get(new_reply.sample.as_ref().unwrap().key_expr.as_keyexpr()) - { - Some(reply) => { - if new_reply.sample.as_ref().unwrap().timestamp - > reply.sample.as_ref().unwrap().timestamp - { + None => key_expr, + }; + let info = DataInfo { + kind: SampleKind::Put, + encoding: Some(m.encoding), + timestamp: m.timestamp, + source_id: m.ext_sinfo.as_ref().map(|i| i.zid), + source_sn: m.ext_sinfo.as_ref().map(|i| i.sn as u64), + }; + let new_reply = Reply { + sample: Ok(Sample::with_info( + key_expr.into_owned(), + m.payload, + Some(info), + )), + replier_id: ZenohId::rand(), // TOTO + }; + let callback = match query.reception_mode { + ConsolidationMode::None => Some((query.callback.clone(), new_reply)), + ConsolidationMode::Monotonic => { + match query + .replies + .as_ref() + .unwrap() + .get(new_reply.sample.as_ref().unwrap().key_expr.as_keyexpr()) + { + Some(reply) => { + if new_reply.sample.as_ref().unwrap().timestamp + > reply.sample.as_ref().unwrap().timestamp + { + query.replies.as_mut().unwrap().insert( + new_reply + .sample + .as_ref() + .unwrap() + .key_expr + .clone() + .into(), + new_reply.clone(), + ); + Some((query.callback.clone(), new_reply)) + } else { + None + } + } + None => { query.replies.as_mut().unwrap().insert( new_reply.sample.as_ref().unwrap().key_expr.clone().into(), new_reply.clone(), ); Some((query.callback.clone(), new_reply)) - } else { - None } } - None => { - query.replies.as_mut().unwrap().insert( - new_reply.sample.as_ref().unwrap().key_expr.clone().into(), - new_reply.clone(), - ); - Some((query.callback.clone(), new_reply)) - } } - } - ConsolidationMode::Latest => { - match query - .replies - .as_ref() - .unwrap() - .get(new_reply.sample.as_ref().unwrap().key_expr.as_keyexpr()) - { - Some(reply) => { - if new_reply.sample.as_ref().unwrap().timestamp - > reply.sample.as_ref().unwrap().timestamp - { + ConsolidationMode::Latest => { + match query + .replies + .as_ref() + .unwrap() + .get(new_reply.sample.as_ref().unwrap().key_expr.as_keyexpr()) + { + Some(reply) => { + if new_reply.sample.as_ref().unwrap().timestamp + > reply.sample.as_ref().unwrap().timestamp + { + query.replies.as_mut().unwrap().insert( + new_reply + .sample + .as_ref() + .unwrap() + .key_expr + .clone() + .into(), + new_reply, + ); + } + } + None => { query.replies.as_mut().unwrap().insert( new_reply.sample.as_ref().unwrap().key_expr.clone().into(), new_reply, ); } - } - None => { - query.replies.as_mut().unwrap().insert( - new_reply.sample.as_ref().unwrap().key_expr.clone().into(), - new_reply, - ); - } - }; - None + }; + None + } + }; + std::mem::drop(state); + if let Some((callback, new_reply)) = callback { + callback(new_reply); } - }; - std::mem::drop(state); - if let Some((callback, new_reply)) = callback { - callback(new_reply); } - } - None => { - log::warn!("Received ReplyData for unkown Query: {}", qid); + None => { + log::warn!("Received ReplyData for unkown Query: {}", msg.rid); + } } } } - fn send_reply_final(&self, qid: ZInt) { - trace!("recv ReplyFinal {:?}", qid); + fn send_response_final(&self, msg: ResponseFinal) { + trace!("recv ResponseFinal {:?}", msg); let mut state = zwrite!(self.state); - match state.queries.get_mut(&qid) { + match state.queries.get_mut(&msg.rid) { Some(query) => { query.nb_final -= 1; if query.nb_final == 0 { - let query = state.queries.remove(&qid).unwrap(); + let query = state.queries.remove(&msg.rid).unwrap(); std::mem::drop(state); if query.reception_mode == ConsolidationMode::Latest { for (_, reply) in query.replies.unwrap().into_iter() { (query.callback)(reply); } } - trace!("Close query {}", qid); + trace!("Close query {}", msg.rid); } } None => { - warn!("Received ReplyFinal for unkown Query: {}", qid); + warn!("Received ResponseFinal for unkown Request: {}", msg.rid); } } } - fn send_pull( - &self, - _is_final: bool, - _key_expr: &WireExpr, - _pull_id: ZInt, - _max_samples: &Option, - ) { - trace!( - "recv Pull {:?} {:?} {:?} {:?}", - _is_final, - _key_expr, - _pull_id, - _max_samples - ); - } - fn send_close(&self) { trace!("recv Close"); } diff --git a/zenoh/src/subscriber.rs b/zenoh/src/subscriber.rs index 8720c7c364..a8a7914f82 100644 --- a/zenoh/src/subscriber.rs +++ b/zenoh/src/subscriber.rs @@ -23,7 +23,7 @@ use std::future::Ready; use std::ops::{Deref, DerefMut}; use std::sync::Arc; use zenoh_core::{AsyncResolve, Resolvable, Resolve, SyncResolve}; -use zenoh_protocol::core::SubInfo; +use zenoh_protocol::network::declare::{subscriber::ext::SubscriberInfo, Mode}; /// The subscription mode. pub use zenoh_protocol::core::SubMode; @@ -257,6 +257,12 @@ impl From for SubMode { } } +impl From for Mode { + fn from(_: PullMode) -> Self { + Mode::Pull + } +} + /// The mode for push subscribers. #[non_exhaustive] #[derive(Debug, Clone, Copy)] @@ -268,6 +274,12 @@ impl From for SubMode { } } +impl From for Mode { + fn from(_: PushMode) -> Self { + Mode::Push + } +} + /// A builder for initializing a [`FlumeSubscriber`]. /// /// # Examples @@ -530,7 +542,7 @@ where &None, self.origin, callback, - &SubInfo { + &SubscriberInfo { reliability: self.reliability, mode: self.mode.into(), }, @@ -582,7 +594,7 @@ where &None, self.origin, callback, - &SubInfo { + &SubscriberInfo { reliability: self.reliability, mode: self.mode.into(), }, diff --git a/zenoh/src/value.rs b/zenoh/src/value.rs index 1e5adca979..849cfd57d5 100644 --- a/zenoh/src/value.rs +++ b/zenoh/src/value.rs @@ -20,7 +20,7 @@ use std::convert::TryFrom; #[cfg(feature = "shared-memory")] use std::sync::Arc; -use zenoh_cfg_properties::Properties; +use zenoh_collections::Properties; use zenoh_result::ZError; use crate::buffers::ZBuf; @@ -50,7 +50,7 @@ impl Value { /// Creates an empty Value. pub fn empty() -> Self { Value { - payload: ZBuf::default(), + payload: ZBuf::empty(), encoding: KnownEncoding::AppOctetStream.into(), } } diff --git a/zenoh/tests/routing.rs b/zenoh/tests/routing.rs index 972893b194..ad8ea1d05a 100644 --- a/zenoh/tests/routing.rs +++ b/zenoh/tests/routing.rs @@ -18,12 +18,11 @@ use std::str::FromStr; use std::sync::atomic::Ordering; use std::sync::{atomic::AtomicUsize, Arc}; use std::time::Duration; -use zenoh::config::{whatami::WhatAmI, Config}; +use zenoh::config::{Config, ModeDependentValue}; use zenoh::prelude::r#async::*; use zenoh::{value::Value, Result}; -use zenoh_config::whatami::WhatAmIMatcher; -use zenoh_config::ModeDependentValue; use zenoh_core::zasync_executor_init; +use zenoh_protocol::core::{WhatAmI, WhatAmIMatcher}; use zenoh_result::{bail, zerror}; const TIMEOUT: Duration = Duration::from_secs(360); diff --git a/zenoh/tests/session.rs b/zenoh/tests/session.rs index 23b84e2309..3e9a9c84eb 100644 --- a/zenoh/tests/session.rs +++ b/zenoh/tests/session.rs @@ -31,7 +31,7 @@ macro_rules! ztimeout { }; } -async fn open_session(endpoints: &[&str]) -> (Session, Session) { +async fn open_session_unicast(endpoints: &[&str]) -> (Session, Session) { // Open the sessions let mut config = config::peer(); config.listen.endpoints = endpoints @@ -39,7 +39,7 @@ async fn open_session(endpoints: &[&str]) -> (Session, Session) { .map(|e| e.parse().unwrap()) .collect::>(); config.scouting.multicast.set_enabled(Some(false)).unwrap(); - println!("[ ][01a] Opening peer01 session"); + println!("[ ][01a] Opening peer01 session: {:?}", endpoints); let peer01 = ztimeout!(zenoh::open(config).res_async()).unwrap(); let mut config = config::peer(); @@ -48,7 +48,24 @@ async fn open_session(endpoints: &[&str]) -> (Session, Session) { .map(|e| e.parse().unwrap()) .collect::>(); config.scouting.multicast.set_enabled(Some(false)).unwrap(); - println!("[ ][02a] Opening peer02 session"); + println!("[ ][02a] Opening peer02 session: {:?}", endpoints); + let peer02 = ztimeout!(zenoh::open(config).res_async()).unwrap(); + + (peer01, peer02) +} + +async fn open_session_multicast(endpoint01: &str, endpoint02: &str) -> (Session, Session) { + // Open the sessions + let mut config = config::peer(); + config.connect.endpoints = vec![endpoint01.parse().unwrap()]; + config.scouting.multicast.set_enabled(Some(true)).unwrap(); + println!("[ ][01a] Opening peer01 session: {}", endpoint01); + let peer01 = ztimeout!(zenoh::open(config).res_async()).unwrap(); + + let mut config = config::peer(); + config.connect.endpoints = vec![endpoint02.parse().unwrap()]; + config.scouting.multicast.set_enabled(Some(true)).unwrap(); + println!("[ ][02a] Opening peer02 session: {}", endpoint02); let peer02 = ztimeout!(zenoh::open(config).res_async()).unwrap(); (peer01, peer02) @@ -61,9 +78,12 @@ async fn close_session(peer01: Session, peer02: Session) { ztimeout!(peer02.close().res_async()).unwrap(); } -async fn test_session_pubsub(peer01: &Session, peer02: &Session) { +async fn test_session_pubsub(peer01: &Session, peer02: &Session, reliability: Reliability) { let key_expr = "test/session"; - + let msg_count = match reliability { + Reliability::Reliable => MSG_COUNT, + Reliability::BestEffort => 1, + }; let msgs = Arc::new(AtomicUsize::new(0)); for size in MSG_SIZE { @@ -76,7 +96,7 @@ async fn test_session_pubsub(peer01: &Session, peer02: &Session) { .declare_subscriber(key_expr) .callback(move |sample| { assert_eq!(sample.value.payload.len(), size); - c_msgs.fetch_add(1, Ordering::SeqCst); + c_msgs.fetch_add(1, Ordering::Relaxed); }) .res_async()) .unwrap(); @@ -86,7 +106,7 @@ async fn test_session_pubsub(peer01: &Session, peer02: &Session) { // Put data println!("[PS][02b] Putting on peer02 session. {MSG_COUNT} msgs of {size} bytes."); - for _ in 0..MSG_COUNT { + for _ in 0..msg_count { ztimeout!(peer02 .put(key_expr, vec![0u8; size]) .congestion_control(CongestionControl::Block) @@ -96,9 +116,9 @@ async fn test_session_pubsub(peer01: &Session, peer02: &Session) { ztimeout!(async { loop { - let cnt = msgs.load(Ordering::SeqCst); - println!("[PS][03b] Received {cnt}/{MSG_COUNT}."); - if cnt < MSG_COUNT { + let cnt = msgs.load(Ordering::Relaxed); + println!("[PS][03b] Received {cnt}/{msg_count}."); + if cnt < msg_count { task::sleep(SLEEP).await; } else { break; @@ -114,13 +134,16 @@ async fn test_session_pubsub(peer01: &Session, peer02: &Session) { } } -async fn test_session_qryrep(peer01: &Session, peer02: &Session) { +async fn test_session_qryrep(peer01: &Session, peer02: &Session, reliability: Reliability) { let key_expr = "test/session"; - + let msg_count = match reliability { + Reliability::Reliable => MSG_COUNT, + Reliability::BestEffort => 1, + }; let msgs = Arc::new(AtomicUsize::new(0)); for size in MSG_SIZE { - msgs.store(0, Ordering::SeqCst); + msgs.store(0, Ordering::Relaxed); // Queryable to data println!("[QR][01c] Queryable on peer01 session"); @@ -128,7 +151,7 @@ async fn test_session_qryrep(peer01: &Session, peer02: &Session) { let qbl = ztimeout!(peer01 .declare_queryable(key_expr) .callback(move |sample| { - c_msgs.fetch_add(1, Ordering::SeqCst); + c_msgs.fetch_add(1, Ordering::Relaxed); let rep = Sample::try_from(key_expr, vec![0u8; size]).unwrap(); task::block_on(async { ztimeout!(sample.reply(Ok(rep)).res_async()).unwrap() }); }) @@ -139,18 +162,18 @@ async fn test_session_qryrep(peer01: &Session, peer02: &Session) { task::sleep(SLEEP).await; // Get data - println!("[QR][02c] Getting on peer02 session. {MSG_COUNT} msgs."); + println!("[QR][02c] Getting on peer02 session. {msg_count} msgs."); let mut cnt = 0; - for _ in 0..MSG_COUNT { + for _ in 0..msg_count { let rs = ztimeout!(peer02.get(key_expr).res_async()).unwrap(); while let Ok(s) = ztimeout!(rs.recv_async()) { assert_eq!(s.sample.unwrap().value.payload.len(), size); cnt += 1; } } - println!("[QR][02c] Got on peer02 session. {cnt}/{MSG_COUNT} msgs."); - assert_eq!(msgs.load(Ordering::SeqCst), MSG_COUNT); - assert_eq!(cnt, MSG_COUNT); + println!("[QR][02c] Got on peer02 session. {cnt}/{msg_count} msgs."); + assert_eq!(msgs.load(Ordering::Relaxed), msg_count); + assert_eq!(cnt, msg_count); println!("[PS][03c] Unqueryable on peer01 session"); ztimeout!(qbl.undeclare().res_async()).unwrap(); @@ -161,14 +184,27 @@ async fn test_session_qryrep(peer01: &Session, peer02: &Session) { } #[test] -fn zenoh_session() { +fn zenoh_session_unicast() { + task::block_on(async { + zasync_executor_init!(); + let _ = env_logger::try_init(); + + let (peer01, peer02) = open_session_unicast(&["tcp/127.0.0.1:17447"]).await; + test_session_pubsub(&peer01, &peer02, Reliability::Reliable).await; + test_session_qryrep(&peer01, &peer02, Reliability::Reliable).await; + close_session(peer01, peer02).await; + }); +} + +#[test] +fn zenoh_session_multicast() { task::block_on(async { zasync_executor_init!(); let _ = env_logger::try_init(); - let (peer01, peer02) = open_session(&["tcp/127.0.0.1:17447"]).await; - test_session_pubsub(&peer01, &peer02).await; - test_session_qryrep(&peer01, &peer02).await; + let (peer01, peer02) = + open_session_multicast("udp/224.0.0.1:17448", "udp/224.0.0.1:17449").await; + test_session_pubsub(&peer01, &peer02, Reliability::BestEffort).await; close_session(peer01, peer02).await; }); } diff --git a/zenohd/src/main.rs b/zenohd/src/main.rs index 729711b442..2b23604c83 100644 --- a/zenohd/src/main.rs +++ b/zenohd/src/main.rs @@ -16,10 +16,9 @@ use clap::{ArgMatches, Command}; use futures::future; use git_version::git_version; use std::collections::HashSet; -use zenoh::config::{ - Config, EndPoint, ModeDependentValue, PermissionsConf, PluginLoad, ValidatedMap, -}; +use zenoh::config::{Config, ModeDependentValue, PermissionsConf, PluginLoad, ValidatedMap}; use zenoh::plugins::PluginsManager; +use zenoh::prelude::{EndPoint, WhatAmI}; use zenoh::runtime::{AdminSpace, Runtime}; const GIT_VERSION: &str = git_version!(prefix = "v", cargo_prefix = "v"); @@ -159,9 +158,7 @@ fn config_from_args(args: &ArgMatches) -> Config { }); if config.mode().is_none() { - config - .set_mode(Some(zenoh::config::WhatAmI::Router)) - .unwrap(); + config.set_mode(Some(WhatAmI::Router)).unwrap(); } if args.occurrences_of("id") > 0 { config