From f94096b5ad503de534307996d674c0375a59257b Mon Sep 17 00:00:00 2001 From: SirCipher Date: Thu, 20 Jun 2024 09:04:12 +0100 Subject: [PATCH 1/4] Adds documentation covering cancellation safety --- .github/workflows/ci.yml | 14 +++++ ratchet_core/Cargo.toml | 4 +- ratchet_core/src/split/mod.rs | 110 ++++++++++++++++++++++++++++------ ratchet_core/src/ws.rs | 76 +++++++++++++++++++++-- ratchet_deflate/Cargo.toml | 4 +- ratchet_ext/Cargo.toml | 2 +- ratchet_fixture/Cargo.toml | 4 +- ratchet_rs/Cargo.toml | 8 +-- 8 files changed, 188 insertions(+), 34 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d9c5c9f..bc66609 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,6 +20,20 @@ jobs: with: command: check + docs: + name: Documentation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: 1.65 + override: true + - uses: actions-rs/cargo@v1 + with: + command: RUSTDOCFLAGS="--cfg docsrs -Dwarnings" cargo doc --lib --no-deps --all-features --workspace + testmsrv: name: Test Suite Latest runs-on: ${{ matrix.os }} diff --git a/ratchet_core/Cargo.toml b/ratchet_core/Cargo.toml index a11bcfd..1baac0b 100644 --- a/ratchet_core/Cargo.toml +++ b/ratchet_core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ratchet_core" -version = "1.0.0" +version = "1.0.1" authors = ["Swim Inc. developers info@swim.ai"] edition = "2021" description = "Async WebSocket implementation" @@ -16,7 +16,7 @@ split = ["futures"] fixture = [] [dependencies] -ratchet_ext = { version = "1.0.0", path = "../ratchet_ext" } +ratchet_ext = { version = "1.0.1", path = "../ratchet_ext" } url = { workspace = true } http = { workspace = true } tokio = { workspace = true, features = ["rt", "net", "io-util"] } diff --git a/ratchet_core/src/split/mod.rs b/ratchet_core/src/split/mod.rs index ca24e76..ef9eeb7 100644 --- a/ratchet_core/src/split/mod.rs +++ b/ratchet_core/src/split/mod.rs @@ -275,7 +275,14 @@ where matches!(self.close_state.load(Ordering::SeqCst), STATE_OPEN) } - /// Constructs a new text WebSocket message with a payload of `data`. + /// Sends a new text WebSocket message with a payload of `data`. + /// + /// # Cancel safety + /// + /// This function is not cancellation safe. If the function is called as part of a Tokio or + /// Futures `select!` macro and another branch completes first, then `data` may have been + /// partially written. To use this function in this fashion, then create the future before the + /// setup of the macro and poll it until completion. pub async fn write_text(&mut self, data: I) -> Result<(), Error> where I: AsRef, @@ -283,7 +290,14 @@ where self.write(data.as_ref(), PayloadType::Text).await } - /// Constructs a new binary WebSocket message with a payload of `data`. + /// Sends a new binary WebSocket message with a payload of `data`. + /// + /// # Cancel safety + /// + /// This function is not cancellation safe. If the function is called as part of a Tokio or + /// Futures `select!` macro and another branch completes first, then `data` may have been + /// partially written. To use this function in this fashion, then create the future before the + /// setup of the macro and poll it until completion. pub async fn write_binary(&mut self, data: I) -> Result<(), Error> where I: AsRef<[u8]>, @@ -291,7 +305,14 @@ where self.write(data.as_ref(), PayloadType::Binary).await } - /// Constructs a new ping WebSocket message with a payload of `data`. + /// Sends a new ping WebSocket message with a payload of `data`. + /// + /// # Cancel safety + /// + /// This function is not cancellation safe. If the function is called as part of a Tokio or + /// Futures `select!` macro and another branch completes first, then `data` may have been + /// partially written. To use this function in this fashion, then create the future before the + /// setup of the macro and poll it until completion. pub async fn write_ping(&mut self, data: I) -> Result<(), Error> where I: AsRef<[u8]>, @@ -299,7 +320,14 @@ where self.write(data.as_ref(), PayloadType::Ping).await } - /// Constructs a new pong WebSocket message with a payload of `data`. + /// Sends a new pong WebSocket message with a payload of `data`. + /// + /// # Cancel safety + /// + /// This function is not cancellation safe. If the function is called as part of a Tokio or + /// Futures `select!` macro and another branch completes first, then `data` may have been + /// partially written. To use this function in this fashion, then create the future before the + /// setup of the macro and poll it until completion. pub async fn write_pong(&mut self, data: I) -> Result<(), Error> where I: AsRef<[u8]>, @@ -307,7 +335,14 @@ where self.write(data.as_ref(), PayloadType::Pong).await } - /// Constructs a new WebSocket message of `message_type` and with a payload of `buf_ref. + /// Sends a new WebSocket message of `message_type` and with a payload of `buf`. + /// + /// # Cancel safety + /// + /// This function is not cancellation safe. If the function is called as part of a Tokio or + /// Futures `select!` macro and another branch completes first, then `buf` may have been + /// partially written. To use this function in this fashion, then create the future before the + /// setup of the macro and poll it until completion. pub async fn write(&mut self, buf: A, message_type: PayloadType) -> Result<(), Error> where A: AsRef<[u8]>, @@ -328,9 +363,16 @@ where .await } - /// Sends a new WebSocket message of `message_type` and with a payload of `buf_ref` and chunked - /// by `fragment_size`. If the length of the buffer is less than the chunk size then only a - /// single message is sent. + /// Constructs a new WebSocket message of `message_type` and with a payload of `buf` and + /// chunked by `fragment_size`. If the length of the buffer is less than the chunk size then + /// only a single message is sent. + /// + /// # Cancel safety + /// + /// This function is not cancellation safe. If the function is called as part of a Tokio or + /// Futures `select!` macro and another branch completes first, then `buf` may have been + /// partially written. To use this function in this fashion, then create the future before the + /// setup of the macro and poll it until completion. pub async fn write_fragmented( &mut self, buf: A, @@ -364,6 +406,13 @@ where /// Close this WebSocket with the reason provided. /// /// If the WebSocket is already closed then `Ok(())` is returned. + /// + /// # Cancel safety + /// + /// This function is not cancellation safe. If the function is called as part of a Tokio or + /// Futures `select!` macro and another branch completes first, then the close frame may have + /// been partially written. To use this function in this fashion, then create the future before + /// the setup of the macro and poll it until completion. pub async fn close(&mut self, reason: CloseReason) -> Result<(), Error> { if !self.is_active() { return Ok(()); @@ -386,6 +435,13 @@ where /// /// It is considered an error if not all bytes could be written due to I/O errors or EOF being /// reached. + /// + /// # Cancel safety + /// + /// This function is not cancellation safe. If the function is called as part of a Tokio or + /// Futures `select!` macro and another branch completes first, then any intermediately buffered + /// contents may have been partially written. To use this function in this fashion, then create + /// the future before the setup of the macro and poll it until completion. pub async fn flush(&mut self) -> Result<(), Error> { if self.is_closed() { return Err(Error::with_cause(ErrorKind::Close, CloseCause::Error)); @@ -418,16 +474,29 @@ where /// received or the error that was produced. /// /// # Errors - /// If an error is produced during a read operation the contents of `read_buffer` must be - /// considered to be dirty. + /// In the event that an error is produced, an attempt is made to cleanly close the connection + /// by sending a close frame to the peer. If this attempt fails, then the connection is + /// abruptly closed and the cause of the error is returned. /// - /// # Note - /// Ratchet transparently handles ping messages received from the peer by returning a pong frame - /// and this function will return `Message::Pong` if one has been received. As per [RFC6455](https://datatracker.ietf.org/doc/html/rfc6455) - /// these may be interleaved between data frames. In the event of one being received while - /// reading a continuation, this function will then yield `Message::Ping` and the `read_buffer` - /// will contain the data received up to that point. The callee must ensure that the contents - /// of `read_buffer` are **not** then modified before calling `read` again. + /// In the event that an error is produced the contents of `read_buffer` must be considered to + /// be dirty; unless the error indicates a clean closure. + /// + /// # Control frames + /// Ratchet transparently handles ping messages received from the peer in read operations by + /// returning a pong frame and this function will return `Message::Pong` if one has been + /// received. As per [RFC6455](https://datatracker.ietf.org/doc/html/rfc6455) these may be + /// interleaved between data frames. In the event of one being received while reading a + /// continuation, this function will then yield `Message::Ping` and the `read_buffer` will + /// contain the data received up to that point. The callee must ensure that the contents of + /// `read_buffer` are **not** then modified before calling `read` again. + /// + /// # Cancel safety + /// + /// This function is not cancellation safe. If the function is called as part of a Tokio or + /// Futures `select!` macro and another branch completes first, then `read_buffer` may have been + /// partially written to and a subsequent read call will fail due to the state of the read being + /// cleared. To use this function in this fashion, then create the future before the setup of + /// the macro and poll it until completion. pub async fn read(&mut self, read_buffer: &mut BytesMut) -> Result { if self.is_closed() { return Err(Error::with_cause(ErrorKind::Close, CloseCause::Error)); @@ -531,6 +600,13 @@ where /// Close this WebSocket with the reason provided. /// /// If the WebSocket is already closed then `Ok(())` is returned. + /// + /// # Cancel safety + /// + /// This function is not cancellation safe. If the function is called as part of a Tokio or + /// Futures `select!` macro and another branch completes first, then the close frame may have + /// been partially written. To use this function in this fashion, then create the future before + /// the setup of the macro and poll it until completion. pub async fn close(&mut self, reason: CloseReason) -> Result<(), Error> { if !self.is_active() { return Ok(()); diff --git a/ratchet_core/src/ws.rs b/ratchet_core/src/ws.rs index bdb8bf0..3b9ae9e 100644 --- a/ratchet_core/src/ws.rs +++ b/ratchet_core/src/ws.rs @@ -173,6 +173,14 @@ where /// continuation, this function will then yield `Message::Ping` and the `read_buffer` will /// contain the data received up to that point. The callee must ensure that the contents of /// `read_buffer` are **not** then modified before calling `read` again. + /// + /// # Cancel safety + /// + /// This function is not cancellation safe. If the function is called as part of a Tokio or + /// Futures `select!` macro and another branch completes first, then `read_buffer` may have been + /// partially written to and a subsequent read call will fail due to the state of the read being + /// cleared. To use this function in this fashion, then create the future before the setup of + /// the macro and poll it until completion. pub async fn read(&mut self, read_buffer: &mut BytesMut) -> Result { if self.is_closed() { return Err(Error::with_cause(ErrorKind::Close, CloseCause::Error)); @@ -240,7 +248,14 @@ where } } - /// Constructs a new text WebSocket message with a payload of `data`. + /// Sends a new text WebSocket message with a payload of `data`. + /// + /// # Cancel safety + /// + /// This function is not cancellation safe. If the function is called as part of a Tokio or + /// Futures `select!` macro and another branch completes first, then `data` may have been + /// partially written. To use this function in this fashion, then create the future before the + /// setup of the macro and poll it until completion. pub async fn write_text(&mut self, data: I) -> Result<(), Error> where I: AsRef, @@ -248,7 +263,14 @@ where self.write(data.as_ref(), PayloadType::Text).await } - /// Constructs a new binary WebSocket message with a payload of `data`. + /// Sends a new binary WebSocket message with a payload of `data`. + /// + /// # Cancel safety + /// + /// This function is not cancellation safe. If the function is called as part of a Tokio or + /// Futures `select!` macro and another branch completes first, then `data` may have been + /// partially written. To use this function in this fashion, then create the future before the + /// setup of the macro and poll it until completion. pub async fn write_binary(&mut self, data: I) -> Result<(), Error> where I: AsRef<[u8]>, @@ -256,7 +278,14 @@ where self.write(data.as_ref(), PayloadType::Binary).await } - /// Constructs a new ping WebSocket message with a payload of `data`. + /// Sends a new ping WebSocket message with a payload of `data`. + /// + /// # Cancel safety + /// + /// This function is not cancellation safe. If the function is called as part of a Tokio or + /// Futures `select!` macro and another branch completes first, then `data` may have been + /// partially written. To use this function in this fashion, then create the future before the + /// setup of the macro and poll it until completion. pub async fn write_ping(&mut self, data: I) -> Result<(), Error> where I: AsRef<[u8]>, @@ -264,7 +293,14 @@ where self.write(data.as_ref(), PayloadType::Ping).await } - /// Constructs a new pong WebSocket message with a payload of `data`. + /// Sends a new pong WebSocket message with a payload of `data`. + /// + /// # Cancel safety + /// + /// This function is not cancellation safe. If the function is called as part of a Tokio or + /// Futures `select!` macro and another branch completes first, then `data` may have been + /// partially written. To use this function in this fashion, then create the future before the + /// setup of the macro and poll it until completion. pub async fn write_pong(&mut self, data: I) -> Result<(), Error> where I: AsRef<[u8]>, @@ -272,7 +308,14 @@ where self.write(data.as_ref(), PayloadType::Pong).await } - /// Constructs a new WebSocket message of `message_type` and with a payload of `buf. + /// Sends a new WebSocket message of `message_type` and with a payload of `buf`. + /// + /// # Cancel safety + /// + /// This function is not cancellation safe. If the function is called as part of a Tokio or + /// Futures `select!` macro and another branch completes first, then `buf` may have been + /// partially written. To use this function in this fashion, then create the future before the + /// setup of the macro and poll it until completion. pub async fn write(&mut self, buf: A, message_type: PayloadType) -> Result<(), Error> where A: AsRef<[u8]>, @@ -321,6 +364,13 @@ where /// Close this WebSocket with the reason provided. /// /// If the WebSocket is already closed then `Ok(())` is returned. + /// + /// # Cancel safety + /// + /// This function is not cancellation safe. If the function is called as part of a Tokio or + /// Futures `select!` macro and another branch completes first, then the close frame may have + /// been partially written. To use this function in this fashion, then create the future before + /// the setup of the macro and poll it until completion. pub async fn close(&mut self, reason: CloseReason) -> Result<(), Error> { if !self.is_active() { return Ok(()); @@ -330,9 +380,16 @@ where self.framed.write_close(reason).await } - /// Constructs a new WebSocket message of `message_type` and with a payload of `buf_ref` and + /// Constructs a new WebSocket message of `message_type` and with a payload of `buf` and /// chunked by `fragment_size`. If the length of the buffer is less than the chunk size then /// only a single message is sent. + /// + /// # Cancel safety + /// + /// This function is not cancellation safe. If the function is called as part of a Tokio or + /// Futures `select!` macro and another branch completes first, then `buf` may have been + /// partially written. To use this function in this fashion, then create the future before the + /// setup of the macro and poll it until completion. pub async fn write_fragmented( &mut self, buf: A, @@ -361,6 +418,13 @@ where /// /// It is considered an error if not all bytes could be written due to I/O errors or EOF being /// reached. + /// + /// # Cancel safety + /// + /// This function is not cancellation safe. If the function is called as part of a Tokio or + /// Futures `select!` macro and another branch completes first, then any intermediately buffered + /// contents may have been partially written. To use this function in this fashion, then create + /// the future before the setup of the macro and poll it until completion. pub async fn flush(&mut self) -> Result<(), Error> { if self.is_closed() { return Err(Error::with_cause(ErrorKind::Close, CloseCause::Error)); diff --git a/ratchet_deflate/Cargo.toml b/ratchet_deflate/Cargo.toml index 74f180e..11c4c6f 100644 --- a/ratchet_deflate/Cargo.toml +++ b/ratchet_deflate/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ratchet_deflate" -version = "1.0.0" +version = "1.0.1" authors = ["Swim Inc. developers info@swim.ai"] edition = "2021" description = "Permessage Deflate for Ratchet" @@ -11,7 +11,7 @@ readme = "README.md" repository = "https://github.com/swimos/ratchet/" [dependencies] -ratchet_ext = { version = "1.0.0", path = "../ratchet_ext" } +ratchet_ext = { version = "1.0.1", path = "../ratchet_ext" } thiserror = { workspace = true } http = { workspace = true } bytes = { workspace = true } diff --git a/ratchet_ext/Cargo.toml b/ratchet_ext/Cargo.toml index 8c7808f..311063c 100644 --- a/ratchet_ext/Cargo.toml +++ b/ratchet_ext/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ratchet_ext" -version = "1.0.0" +version = "1.0.1" authors = ["Swim Inc. developers info@swim.ai"] edition = "2021" description = "WebSocket extensions for Ratchet" diff --git a/ratchet_fixture/Cargo.toml b/ratchet_fixture/Cargo.toml index a9e8c98..feb7def 100644 --- a/ratchet_fixture/Cargo.toml +++ b/ratchet_fixture/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ratchet_fixture" -version = "1.0.0" +version = "1.0.1" edition = "2021" description = "Ratchet fixture" keywords = ["websocket", "async", "futures", "tokio"] @@ -10,7 +10,7 @@ readme = "README.md" repository = "https://github.com/swimos/ratchet/" [dependencies] -ratchet = { package = "ratchet_rs", version = "1.0.0", path = "../ratchet_rs", features = ["split", "deflate", "fixture"] } +ratchet = { package = "ratchet_rs", version = "1.0.1", path = "../ratchet_rs", features = ["split", "deflate", "fixture"] } tokio = { workspace = true, features = ["io-util"] } bytes = { workspace = true } futures = { workspace = true } diff --git a/ratchet_rs/Cargo.toml b/ratchet_rs/Cargo.toml index 16840a9..73e4432 100644 --- a/ratchet_rs/Cargo.toml +++ b/ratchet_rs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ratchet_rs" -version = "1.0.0" +version = "1.0.1" authors = ["Swim Inc. developers info@swim.ai"] edition = "2021" description = "Async WebSocket implementation" @@ -17,9 +17,9 @@ split = ["ratchet_core/split"] fixture = ["ratchet_core/fixture"] [dependencies] -ratchet_core = { version = "1.0.0", path = "../ratchet_core" } -ratchet_ext = { version = "1.0.0", path = "../ratchet_ext" } -ratchet_deflate = { version = "1.0.0", path = "../ratchet_deflate", optional = true } +ratchet_core = { version = "1.0.1", path = "../ratchet_core" } +ratchet_ext = { version = "1.0.1", path = "../ratchet_ext" } +ratchet_deflate = { version = "1.0.1", path = "../ratchet_deflate", optional = true } tracing-subscriber = { workspace = true, features = ["env-filter"] } log = { workspace = true } From 26d90c10d775f400044b7104aa7b82a481dfad1e Mon Sep 17 00:00:00 2001 From: SirCipher Date: Thu, 20 Jun 2024 09:21:50 +0100 Subject: [PATCH 2/4] Update workflow --- .github/workflows/ci.yml | 131 ++++++++++++++------------------------- 1 file changed, 48 insertions(+), 83 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc66609..ef06e00 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,33 +6,20 @@ on: name: Continuous integration jobs: - check: - name: Check - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: 1.65 - override: true - - uses: actions-rs/cargo@v1 - with: - command: check - docs: name: Documentation runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable with: - profile: minimal toolchain: 1.65 - override: true - - uses: actions-rs/cargo@v1 - with: - command: RUSTDOCFLAGS="--cfg docsrs -Dwarnings" cargo doc --lib --no-deps --all-features --workspace + - uses: Swatinem/rust-cache@v2 + - name: Build Documentation + run: cargo doc --lib --no-deps --all-features --workspace + env: + RUSTDOCFLAGS: --cfg docsrs -Dwarnings testmsrv: name: Test Suite Latest @@ -45,15 +32,13 @@ jobs: - macos-latest steps: - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable with: - profile: minimal toolchain: 1.78.0 - override: true - - uses: actions-rs/cargo@v1 - with: - command: test - args: --all-features --all-targets --workspace + - uses: Swatinem/rust-cache@v2 + - name: Test + run: cargo test --all-features --all-targets --workspace testlatest: name: Test Suite MSRV @@ -66,104 +51,84 @@ jobs: - macos-latest steps: - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable with: - profile: minimal toolchain: 1.65 - override: true - - uses: actions-rs/cargo@v1 - with: - command: test - args: --all-features --all-targets --workspace + - uses: Swatinem/rust-cache@v2 + - name: Test + run: cargo test --all-features --all-targets --workspace fmt: name: Rustfmt runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable with: - profile: minimal toolchain: 1.65 - override: true - - run: rustup component add rustfmt - - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check + components: rustfmt + - uses: Swatinem/rust-cache@v2 + - run: cargo fmt --all -- --check clippy: name: Clippy runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable with: - profile: minimal - toolchain: 1.65 - override: true - - run: rustup component add clippy - - uses: actions-rs/cargo@v1 - with: - command: clippy - args: --all-features --workspace -- -D warnings + toolchain: 1.65.0 + components: clippy + - uses: Swatinem/rust-cache@v2 + - run: cargo clippy --all-features --all-targets --workspace -- -D warnings autobahnclient: name: Autobahn Client runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable with: - profile: minimal - toolchain: 1.65 - override: true - - uses: actions-rs/cargo@v1 - with: - command: run - args: --bin client --release + toolchain: 1.65.0 + - uses: Swatinem/rust-cache@v2 + - run: cargo run --bin client --release autobahnserver: name: Autobahn Server runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: 1.65 - override: true - - uses: actions-rs/cargo@v1 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable with: - command: run - args: --bin server --release + toolchain: 1.65.0 + - uses: Swatinem/rust-cache@v2 + - run: cargo run --bin server --release autobahnclientsplit: name: Autobahn Client Split Socket runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: 1.65 - override: true - - uses: actions-rs/cargo@v1 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable with: - command: run - args: --bin split_client --release + toolchain: 1.65.0 + - uses: Swatinem/rust-cache@v2 + - run: cargo run --bin split_client --release autobahnserversplit: name: Autobahn Server Split Socket runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: 1.65 - override: true - - uses: actions-rs/cargo@v1 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable with: - command: run - args: --bin split_server --release \ No newline at end of file + toolchain: 1.65.0 + - uses: Swatinem/rust-cache@v2 + - run: cargo run --bin split_server --release \ No newline at end of file From 99f50b04cf46b250b71115dfe56e7c0ba2923b9f Mon Sep 17 00:00:00 2001 From: SirCipher Date: Thu, 20 Jun 2024 09:28:27 +0100 Subject: [PATCH 3/4] Bumps clippy CI version --- .github/workflows/ci.yml | 3 ++- ratchet_core/src/framed/tests.rs | 4 ++-- ratchet_core/src/protocol/tests.rs | 16 ++++++++-------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ef06e00..48800cf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -80,7 +80,8 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.65.0 + # Not the same as the MSRV as an ICE is thrown + toolchain: 1.66.0 components: clippy - uses: Swatinem/rust-cache@v2 - run: cargo clippy --all-features --all-targets --workspace -- -D warnings diff --git a/ratchet_core/src/framed/tests.rs b/ratchet_core/src/framed/tests.rs index 64896fb..96fe1fb 100644 --- a/ratchet_core/src/framed/tests.rs +++ b/ratchet_core/src/framed/tests.rs @@ -200,7 +200,7 @@ where #[tokio::test] async fn ping() { - let buffer = BytesMut::from_iter(&[137, 4, 1, 2, 3, 4]); + let buffer = BytesMut::from_iter([137, 4, 1, 2, 3, 4]); let mut framed = FramedIo::new(EmptyIo, buffer, Role::Client, usize::MAX, 0); ok_eq( @@ -211,7 +211,7 @@ async fn ping() { #[tokio::test] async fn pong() { - let buffer = BytesMut::from_iter(&[138, 4, 1, 2, 3, 4]); + let buffer = BytesMut::from_iter([138, 4, 1, 2, 3, 4]); let mut framed = FramedIo::new(EmptyIo, buffer, Role::Client, usize::MAX, 0); ok_eq( diff --git a/ratchet_core/src/protocol/tests.rs b/ratchet_core/src/protocol/tests.rs index 03e744c..1d54cf9 100644 --- a/ratchet_core/src/protocol/tests.rs +++ b/ratchet_core/src/protocol/tests.rs @@ -163,7 +163,7 @@ mod decode { #[test] fn header() { - let bytes = BytesMut::from_iter(&[129, 4, 1, 2, 3, 4]); + let bytes = BytesMut::from_iter([129, 4, 1, 2, 3, 4]); let (header, _header_len, _payload_len) = FrameHeader::read_from(&bytes, false, 0, usize::MAX) .unwrap() @@ -181,15 +181,15 @@ mod decode { #[test] fn rsv() { - let bytes = BytesMut::from_iter(&[161, 4, 1, 2, 3, 4]); + let bytes = BytesMut::from_iter([161, 4, 1, 2, 3, 4]); let r = FrameHeader::read_from(&bytes, false, 0, usize::MAX); expect_protocol_error(r, ProtocolError::UnknownExtension); - let bytes = BytesMut::from_iter(&[161, 4, 1, 2, 3, 4]); + let bytes = BytesMut::from_iter([161, 4, 1, 2, 3, 4]); let r = FrameHeader::read_from(&bytes, false, 1 << 6 & 1 << 4, usize::MAX); expect_protocol_error(r, ProtocolError::UnknownExtension); - let bytes = BytesMut::from_iter(&[193, 4, 1, 2, 3, 4]); + let bytes = BytesMut::from_iter([193, 4, 1, 2, 3, 4]); let result = FrameHeader::read_from(&bytes, false, 1 << 6, usize::MAX); let _expected = FrameHeader { @@ -202,28 +202,28 @@ mod decode { #[test] fn overflow() { - let bytes = BytesMut::from_iter(&[129, 4, 1, 2, 3, 4]); + let bytes = BytesMut::from_iter([129, 4, 1, 2, 3, 4]); let r = FrameHeader::read_from(&bytes, false, 0, 1); expect_protocol_error(r, ProtocolError::FrameOverflow); } #[test] fn fragmented_control() { - let bytes = BytesMut::from_iter(&[8, 4, 1, 2, 3, 4]); + let bytes = BytesMut::from_iter([8, 4, 1, 2, 3, 4]); let r = FrameHeader::read_from(&bytes, false, 0, usize::MAX); expect_protocol_error(r, ProtocolError::FragmentedControl); } #[test] fn unmasked() { - let bytes = BytesMut::from_iter(&[1, 132, 0, 0, 0, 0, 1, 2, 3, 4]); + let bytes = BytesMut::from_iter([1, 132, 0, 0, 0, 0, 1, 2, 3, 4]); let r = FrameHeader::read_from(&bytes, false, 0, usize::MAX); expect_protocol_error(r, ProtocolError::MaskedFrame); } #[test] fn masked_err() { - let bytes = BytesMut::from_iter(&[129, 4, 1, 2, 3, 4]); + let bytes = BytesMut::from_iter([129, 4, 1, 2, 3, 4]); let r = FrameHeader::read_from(&bytes, true, 0, usize::MAX); expect_protocol_error(r, ProtocolError::UnmaskedFrame); } From eec2d627114adb5a1e7e086070770183f297f8f2 Mon Sep 17 00:00:00 2001 From: SirCipher Date: Thu, 20 Jun 2024 10:11:18 +0100 Subject: [PATCH 4/4] updates cancellation safety documentation --- ratchet_core/src/split/mod.rs | 78 +++++++++++++++-------------------- ratchet_core/src/ws.rs | 70 ++++++++++++++----------------- 2 files changed, 64 insertions(+), 84 deletions(-) diff --git a/ratchet_core/src/split/mod.rs b/ratchet_core/src/split/mod.rs index ef9eeb7..50379b6 100644 --- a/ratchet_core/src/split/mod.rs +++ b/ratchet_core/src/split/mod.rs @@ -279,10 +279,10 @@ where /// /// # Cancel safety /// - /// This function is not cancellation safe. If the function is called as part of a Tokio or - /// Futures `select!` macro and another branch completes first, then `data` may have been - /// partially written. To use this function in this fashion, then create the future before the - /// setup of the macro and poll it until completion. + /// This function is not cancellation safe. If the future is dropped before it has completed + /// then the connection state is undefined. It may not be possible to recover the connection due + /// the write operation having written only part of the frame and the state of the write + /// operation has been lost. pub async fn write_text(&mut self, data: I) -> Result<(), Error> where I: AsRef, @@ -294,10 +294,10 @@ where /// /// # Cancel safety /// - /// This function is not cancellation safe. If the function is called as part of a Tokio or - /// Futures `select!` macro and another branch completes first, then `data` may have been - /// partially written. To use this function in this fashion, then create the future before the - /// setup of the macro and poll it until completion. + /// This function is not cancellation safe. If the future is dropped before it has completed + /// then the connection state is undefined. It may not be possible to recover the connection due + /// the write operation having written only part of the frame and the state of the write + /// operation has been lost. pub async fn write_binary(&mut self, data: I) -> Result<(), Error> where I: AsRef<[u8]>, @@ -309,10 +309,10 @@ where /// /// # Cancel safety /// - /// This function is not cancellation safe. If the function is called as part of a Tokio or - /// Futures `select!` macro and another branch completes first, then `data` may have been - /// partially written. To use this function in this fashion, then create the future before the - /// setup of the macro and poll it until completion. + /// This function is not cancellation safe. If the future is dropped before it has completed + /// then the connection state is undefined. It may not be possible to recover the connection due + /// the write operation having written only part of the control frame and the state of the write + /// operation has been lost. pub async fn write_ping(&mut self, data: I) -> Result<(), Error> where I: AsRef<[u8]>, @@ -324,10 +324,10 @@ where /// /// # Cancel safety /// - /// This function is not cancellation safe. If the function is called as part of a Tokio or - /// Futures `select!` macro and another branch completes first, then `data` may have been - /// partially written. To use this function in this fashion, then create the future before the - /// setup of the macro and poll it until completion. + /// This function is not cancellation safe. If the future is dropped before it has completed + /// then the connection state is undefined. It may not be possible to recover the connection due + /// the write operation having written only part of the control frame and the state of the write + /// operation has been lost. pub async fn write_pong(&mut self, data: I) -> Result<(), Error> where I: AsRef<[u8]>, @@ -339,10 +339,9 @@ where /// /// # Cancel safety /// - /// This function is not cancellation safe. If the function is called as part of a Tokio or - /// Futures `select!` macro and another branch completes first, then `buf` may have been - /// partially written. To use this function in this fashion, then create the future before the - /// setup of the macro and poll it until completion. + /// This function is not cancellation safe. If the future is dropped before it has completed + /// then both `buf` and the connection state are undefined. It may not be possible to recover + /// the connection due the read operation partially completing and the state has been lost. pub async fn write(&mut self, buf: A, message_type: PayloadType) -> Result<(), Error> where A: AsRef<[u8]>, @@ -369,10 +368,10 @@ where /// /// # Cancel safety /// - /// This function is not cancellation safe. If the function is called as part of a Tokio or - /// Futures `select!` macro and another branch completes first, then `buf` may have been - /// partially written. To use this function in this fashion, then create the future before the - /// setup of the macro and poll it until completion. + /// This function is not cancellation safe. If the future is dropped before it has completed + /// then the connection state is undefined. It may not be possible to recover the connection due + /// the write operation having written only part of the buffer and the state of the write + /// operation has been lost. pub async fn write_fragmented( &mut self, buf: A, @@ -409,10 +408,10 @@ where /// /// # Cancel safety /// - /// This function is not cancellation safe. If the function is called as part of a Tokio or - /// Futures `select!` macro and another branch completes first, then the close frame may have - /// been partially written. To use this function in this fashion, then create the future before - /// the setup of the macro and poll it until completion. + /// This function is not cancellation safe. If the future is dropped before it has completed + /// then the connection state is undefined. It may not be possible to recover the connection due + /// the write operation having written only part of the close frame and the state of the write + /// operation has been lost. pub async fn close(&mut self, reason: CloseReason) -> Result<(), Error> { if !self.is_active() { return Ok(()); @@ -435,13 +434,6 @@ where /// /// It is considered an error if not all bytes could be written due to I/O errors or EOF being /// reached. - /// - /// # Cancel safety - /// - /// This function is not cancellation safe. If the function is called as part of a Tokio or - /// Futures `select!` macro and another branch completes first, then any intermediately buffered - /// contents may have been partially written. To use this function in this fashion, then create - /// the future before the setup of the macro and poll it until completion. pub async fn flush(&mut self) -> Result<(), Error> { if self.is_closed() { return Err(Error::with_cause(ErrorKind::Close, CloseCause::Error)); @@ -492,11 +484,9 @@ where /// /// # Cancel safety /// - /// This function is not cancellation safe. If the function is called as part of a Tokio or - /// Futures `select!` macro and another branch completes first, then `read_buffer` may have been - /// partially written to and a subsequent read call will fail due to the state of the read being - /// cleared. To use this function in this fashion, then create the future before the setup of - /// the macro and poll it until completion. + /// This function is not cancellation safe. If the future is dropped before it has completed + /// then both `buf` and the connection state are undefined. It may not be possible to recover + /// the connection due the read operation partially completing and the state has been lost. pub async fn read(&mut self, read_buffer: &mut BytesMut) -> Result { if self.is_closed() { return Err(Error::with_cause(ErrorKind::Close, CloseCause::Error)); @@ -603,10 +593,10 @@ where /// /// # Cancel safety /// - /// This function is not cancellation safe. If the function is called as part of a Tokio or - /// Futures `select!` macro and another branch completes first, then the close frame may have - /// been partially written. To use this function in this fashion, then create the future before - /// the setup of the macro and poll it until completion. + /// This function is not cancellation safe. If the future is dropped before it has completed + /// then the connection state is undefined. It may not be possible to recover the connection due + /// the write operation having written only part of the close frame and the state of the write + /// operation has been lost. pub async fn close(&mut self, reason: CloseReason) -> Result<(), Error> { if !self.is_active() { return Ok(()); diff --git a/ratchet_core/src/ws.rs b/ratchet_core/src/ws.rs index 3b9ae9e..e07f635 100644 --- a/ratchet_core/src/ws.rs +++ b/ratchet_core/src/ws.rs @@ -176,11 +176,9 @@ where /// /// # Cancel safety /// - /// This function is not cancellation safe. If the function is called as part of a Tokio or - /// Futures `select!` macro and another branch completes first, then `read_buffer` may have been - /// partially written to and a subsequent read call will fail due to the state of the read being - /// cleared. To use this function in this fashion, then create the future before the setup of - /// the macro and poll it until completion. + /// This function is not cancellation safe. If the future is dropped before it has completed + /// then both `buf` and the connection state are undefined. It may not be possible to recover + /// the connection due the read operation partially completing and the state has been lost. pub async fn read(&mut self, read_buffer: &mut BytesMut) -> Result { if self.is_closed() { return Err(Error::with_cause(ErrorKind::Close, CloseCause::Error)); @@ -252,10 +250,10 @@ where /// /// # Cancel safety /// - /// This function is not cancellation safe. If the function is called as part of a Tokio or - /// Futures `select!` macro and another branch completes first, then `data` may have been - /// partially written. To use this function in this fashion, then create the future before the - /// setup of the macro and poll it until completion. + /// This function is not cancellation safe. If the future is dropped before it has completed + /// then the connection state is undefined. It may not be possible to recover the connection due + /// the write operation having written only part of the frame and the state of the write + /// operation has been lost. pub async fn write_text(&mut self, data: I) -> Result<(), Error> where I: AsRef, @@ -267,10 +265,10 @@ where /// /// # Cancel safety /// - /// This function is not cancellation safe. If the function is called as part of a Tokio or - /// Futures `select!` macro and another branch completes first, then `data` may have been - /// partially written. To use this function in this fashion, then create the future before the - /// setup of the macro and poll it until completion. + /// This function is not cancellation safe. If the future is dropped before it has completed + /// then the connection state is undefined. It may not be possible to recover the connection due + /// the write operation having written only part of the frame and the state of the write + /// operation has been lost. pub async fn write_binary(&mut self, data: I) -> Result<(), Error> where I: AsRef<[u8]>, @@ -282,10 +280,10 @@ where /// /// # Cancel safety /// - /// This function is not cancellation safe. If the function is called as part of a Tokio or - /// Futures `select!` macro and another branch completes first, then `data` may have been - /// partially written. To use this function in this fashion, then create the future before the - /// setup of the macro and poll it until completion. + /// This function is not cancellation safe. If the future is dropped before it has completed + /// then the connection state is undefined. It may not be possible to recover the connection due + /// the write operation having written only part of the control frame and the state of the write + /// operation has been lost. pub async fn write_ping(&mut self, data: I) -> Result<(), Error> where I: AsRef<[u8]>, @@ -297,10 +295,10 @@ where /// /// # Cancel safety /// - /// This function is not cancellation safe. If the function is called as part of a Tokio or - /// Futures `select!` macro and another branch completes first, then `data` may have been - /// partially written. To use this function in this fashion, then create the future before the - /// setup of the macro and poll it until completion. + /// This function is not cancellation safe. If the future is dropped before it has completed + /// then the connection state is undefined. It may not be possible to recover the connection due + /// the write operation having written only part of the control frame and the state of the write + /// operation has been lost. pub async fn write_pong(&mut self, data: I) -> Result<(), Error> where I: AsRef<[u8]>, @@ -312,10 +310,9 @@ where /// /// # Cancel safety /// - /// This function is not cancellation safe. If the function is called as part of a Tokio or - /// Futures `select!` macro and another branch completes first, then `buf` may have been - /// partially written. To use this function in this fashion, then create the future before the - /// setup of the macro and poll it until completion. + /// This function is not cancellation safe. If the future is dropped before it has completed + /// then both `buf` and the connection state are undefined. It may not be possible to recover + /// the connection due the read operation partially completing and the state has been lost. pub async fn write(&mut self, buf: A, message_type: PayloadType) -> Result<(), Error> where A: AsRef<[u8]>, @@ -367,10 +364,10 @@ where /// /// # Cancel safety /// - /// This function is not cancellation safe. If the function is called as part of a Tokio or - /// Futures `select!` macro and another branch completes first, then the close frame may have - /// been partially written. To use this function in this fashion, then create the future before - /// the setup of the macro and poll it until completion. + /// This function is not cancellation safe. If the future is dropped before it has completed + /// then the connection state is undefined. It may not be possible to recover the connection due + /// the write operation having written only part of the close frame and the state of the write + /// operation has been lost. pub async fn close(&mut self, reason: CloseReason) -> Result<(), Error> { if !self.is_active() { return Ok(()); @@ -386,10 +383,10 @@ where /// /// # Cancel safety /// - /// This function is not cancellation safe. If the function is called as part of a Tokio or - /// Futures `select!` macro and another branch completes first, then `buf` may have been - /// partially written. To use this function in this fashion, then create the future before the - /// setup of the macro and poll it until completion. + /// This function is not cancellation safe. If the future is dropped before it has completed + /// then the connection state is undefined. It may not be possible to recover the connection due + /// the write operation having written only part of the buffer and the state of the write + /// operation has been lost. pub async fn write_fragmented( &mut self, buf: A, @@ -418,13 +415,6 @@ where /// /// It is considered an error if not all bytes could be written due to I/O errors or EOF being /// reached. - /// - /// # Cancel safety - /// - /// This function is not cancellation safe. If the function is called as part of a Tokio or - /// Futures `select!` macro and another branch completes first, then any intermediately buffered - /// contents may have been partially written. To use this function in this fashion, then create - /// the future before the setup of the macro and poll it until completion. pub async fn flush(&mut self) -> Result<(), Error> { if self.is_closed() { return Err(Error::with_cause(ErrorKind::Close, CloseCause::Error));