From 2790d34ccde25b0bd219c302a116550ba7092faa Mon Sep 17 00:00:00 2001 From: willrnch <1873880+willrnch@users.noreply.github.com> Date: Mon, 3 Nov 2025 14:01:39 +0100 Subject: [PATCH] permessage-deflate --- Cargo.lock | 5 +++-- Cargo.toml | 1 + autobahn/Makefile | 6 +++--- src/error.rs | 2 ++ src/lib.rs | 37 +++++++++++++++++++++++++++++++++++-- 5 files changed, 44 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7025f80..fc103cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -409,6 +409,7 @@ dependencies = [ "http-body-util", "hyper", "hyper-util", + "miniz_oxide", "pin-project", "rand", "rustls-pemfile", @@ -726,9 +727,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.8.3" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] diff --git a/Cargo.toml b/Cargo.toml index 13bb401..37ac357 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,7 @@ utf-8 = "0.7.5" rand = "0.8.4" thiserror = "1.0.40" bytes = "1.5.0" +miniz_oxide = "0.8.9" # Axum integration axum-core = { version = "0.5.0", optional = true } diff --git a/autobahn/Makefile b/autobahn/Makefile index 9e20c4b..54c09e3 100644 --- a/autobahn/Makefile +++ b/autobahn/Makefile @@ -1,7 +1,7 @@ AUTOBAHN_TESTSUITE_DOCKER := crossbario/autobahn-testsuite:0.8.2@sha256:5d4ba3aa7d6ab2fdbf6606f3f4ecbe4b66f205ce1cbc176d6cdf650157e52242 build-server: - sudo cargo build --release --example echo_server --features "upgrade" + cargo build --release --example echo_server --features "upgrade" run-server: build-server echo ${PWD} @@ -18,7 +18,7 @@ run-server: build-server ../target/release/examples/echo_server build-client: - sudo cargo build --release --example autobahn_client --features "upgrade" + cargo build --release --example autobahn_client --features "upgrade" run-client: build-client echo ${PWD} @@ -34,4 +34,4 @@ run-client: build-client sleep 5 ../target/release/examples/autobahn_client -.PHONY: build-server run-server build-client run-client \ No newline at end of file +.PHONY: build-server run-server build-client run-client diff --git a/src/error.rs b/src/error.rs index 848116a..460703f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -34,6 +34,8 @@ pub enum WebSocketError { InvalidSecWebsocketVersion, #[error("Invalid value")] InvalidValue, + #[error("Invalid encoding")] + InvalidEncoding, #[error("Sec-WebSocket-Key header is missing")] MissingSecWebSocketKey, #[error(transparent)] diff --git a/src/lib.rs b/src/lib.rs index eefc955..7955fea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -175,6 +175,9 @@ use tokio::io::AsyncReadExt; use tokio::io::AsyncWrite; use tokio::io::AsyncWriteExt; +use miniz_oxide::{DataFormat, MZFlush}; +use miniz_oxide::inflate::stream::{InflateState, inflate}; + pub use crate::close::CloseCode; pub use crate::error::WebSocketError; pub use crate::fragment::FragmentCollector; @@ -681,7 +684,11 @@ impl ReadHalf { let rsv2 = self.buffer[0] & 0b00100000 != 0; let rsv3 = self.buffer[0] & 0b00010000 != 0; - if rsv1 || rsv2 || rsv3 { + let mut compressed = false; + + if rsv1 && !rsv2 && !rsv3 { + compressed = true; + } else if rsv1 || rsv2 || rsv3 { return Err(WebSocketError::ReservedBitsNotZero); } @@ -743,7 +750,11 @@ impl ReadHalf { } // if we read too much it will stay in the buffer, for the next call to this method - let payload = self.buffer.split_to(payload_len); + let mut payload = self.buffer.split_to(payload_len); + if compressed { + payload = BytesMut::from(inflate_payload(&payload.to_vec())?.as_slice()); + } + let frame = Frame::new(fin, opcode, mask, Payload::Bytes(payload)); Ok(frame) } @@ -820,3 +831,25 @@ mod tests { assert_unsync::>(); }; } + +fn inflate_payload( + payload: &Vec +) -> Result, WebSocketError> +{ + let max_output_size = usize::max_value(); + let mut out: Vec = vec![0; payload.len().saturating_mul(2).min(max_output_size)]; + let mut state = InflateState::new_boxed(DataFormat::Raw); + + let payload = [payload.as_slice(), [0x00, 0x00, 0xff, 0xff].as_slice()].concat(); + let res = inflate(&mut state, &payload, &mut out, MZFlush::Partial); + + match res.status { + Ok(_) => { + out.truncate(res.bytes_written); + Ok(out) + } + Err(_) => { + Err(WebSocketError::InvalidEncoding) + } + } +}