Skip to content

Commit f8a864d

Browse files
committed
permessage-deflate
1 parent f43e929 commit f8a864d

8 files changed

Lines changed: 77 additions & 10 deletions

File tree

Cargo.lock

Lines changed: 4 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ utf-8 = "0.7.5"
5454
rand = "0.8.4"
5555
thiserror = "1.0.40"
5656
bytes = "1.5.0"
57+
miniz_oxide = "0.8.9"
5758

5859
# Axum integration
5960
axum-core = { version = "0.5.0", optional = true }

autobahn/Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
AUTOBAHN_TESTSUITE_DOCKER := crossbario/autobahn-testsuite:0.8.2@sha256:5d4ba3aa7d6ab2fdbf6606f3f4ecbe4b66f205ce1cbc176d6cdf650157e52242
22

33
build-server:
4-
sudo cargo build --release --example echo_server --features "upgrade"
4+
cargo build --release --example echo_server --features "upgrade"
55

66
run-server: build-server
77
echo ${PWD}
@@ -18,7 +18,7 @@ run-server: build-server
1818
../target/release/examples/echo_server
1919

2020
build-client:
21-
sudo cargo build --release --example autobahn_client --features "upgrade"
21+
cargo build --release --example autobahn_client --features "upgrade"
2222

2323
run-client: build-client
2424
echo ${PWD}
@@ -34,4 +34,4 @@ run-client: build-client
3434
sleep 5
3535
../target/release/examples/autobahn_client
3636

37-
.PHONY: build-server run-server build-client run-client
37+
.PHONY: build-server run-server build-client run-client

rust-toolchain

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.76.0
1+
1.91.1

src/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ pub enum WebSocketError {
3434
InvalidSecWebsocketVersion,
3535
#[error("Invalid value")]
3636
InvalidValue,
37+
#[error("Invalid encoding")]
38+
InvalidEncoding,
3739
#[error("Sec-WebSocket-Key header is missing")]
3840
MissingSecWebSocketKey,
3941
#[error(transparent)]

src/fragment.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ impl Fragments {
222222
if self.fragments.is_some() {
223223
return Err(WebSocketError::InvalidFragment);
224224
}
225-
return Ok(Some(Frame::new(true, frame.opcode, None, frame.payload)));
225+
return Ok(Some(Frame::new(true, frame.opcode, None, frame.payload, frame.compressed)));
226226
} else {
227227
self.fragments = match frame.opcode {
228228
OpCode::Text => match utf8::decode(&frame.payload) {
@@ -292,6 +292,7 @@ impl Fragments {
292292
self.opcode,
293293
None,
294294
self.fragments.take().unwrap().take_buffer().into(),
295+
false,
295296
)));
296297
}
297298
}
@@ -303,6 +304,7 @@ impl Fragments {
303304
self.opcode,
304305
None,
305306
self.fragments.take().unwrap().take_buffer().into(),
307+
false,
306308
)));
307309
}
308310
}

src/frame.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,16 @@
1414

1515
use tokio::io::AsyncWriteExt;
1616

17+
use miniz_oxide::{MZFlush, MZStatus};
18+
use miniz_oxide::inflate::stream::{InflateState, inflate};
19+
1720
use bytes::BytesMut;
1821
use core::ops::Deref;
1922

2023
use crate::WebSocketError;
2124

25+
const TRAILER: [u8; 4] = [0x00, 0x00, 0xff, 0xff];
26+
2227
macro_rules! repr_u8 {
2328
($(#[$meta:meta])* $vis:vis enum $name:ident {
2429
$($(#[$vmeta:meta])* $vname:ident $(= $val:expr)?,)*
@@ -136,6 +141,8 @@ pub struct Frame<'f> {
136141
mask: Option<[u8; 4]>,
137142
/// The payload of the frame.
138143
pub payload: Payload<'f>,
144+
/// Is the frame payload compressed
145+
pub compressed: bool,
139146
}
140147

141148
const MAX_HEAD_SIZE: usize = 16;
@@ -147,12 +154,14 @@ impl<'f> Frame<'f> {
147154
opcode: OpCode,
148155
mask: Option<[u8; 4]>,
149156
payload: Payload<'f>,
157+
compressed: bool,
150158
) -> Self {
151159
Self {
152160
fin,
153161
opcode,
154162
mask,
155163
payload,
164+
compressed,
156165
}
157166
}
158167

@@ -167,6 +176,7 @@ impl<'f> Frame<'f> {
167176
opcode: OpCode::Text,
168177
mask: None,
169178
payload,
179+
compressed: false,
170180
}
171181
}
172182

@@ -179,6 +189,7 @@ impl<'f> Frame<'f> {
179189
opcode: OpCode::Binary,
180190
mask: None,
181191
payload,
192+
compressed: false,
182193
}
183194
}
184195

@@ -197,6 +208,7 @@ impl<'f> Frame<'f> {
197208
opcode: OpCode::Close,
198209
mask: None,
199210
payload: payload.into(),
211+
compressed: false,
200212
}
201213
}
202214

@@ -211,6 +223,7 @@ impl<'f> Frame<'f> {
211223
opcode: OpCode::Close,
212224
mask: None,
213225
payload,
226+
compressed: false,
214227
}
215228
}
216229

@@ -223,6 +236,7 @@ impl<'f> Frame<'f> {
223236
opcode: OpCode::Pong,
224237
mask: None,
225238
payload,
239+
compressed: false,
226240
}
227241
}
228242

@@ -334,6 +348,33 @@ impl<'f> Frame<'f> {
334348
buf[size..size + len].copy_from_slice(&self.payload);
335349
&buf[..size + len]
336350
}
351+
352+
pub fn inflate(&self, state: &mut InflateState) -> Result<Self, WebSocketError>
353+
{
354+
let payload = [self.payload.to_vec().as_slice(), &TRAILER].concat();
355+
356+
let max_output_size = usize::max_value();
357+
let mut out: Vec<u8> = vec![0; payload.len().saturating_mul(2).min(max_output_size)];
358+
359+
let res = inflate(state, &payload, &mut out, MZFlush::None);
360+
361+
if res.status != Ok(MZStatus::Ok) {
362+
return Err(WebSocketError::InvalidEncoding);
363+
}
364+
365+
out.truncate(res.bytes_written);
366+
367+
let payload = Payload::Owned(out);
368+
369+
Ok(Self {
370+
fin: self.fin,
371+
opcode: self.opcode,
372+
mask: self.mask,
373+
payload,
374+
compressed: false
375+
})
376+
}
377+
337378
}
338379

339380
repr_u8! {

src/lib.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,9 @@ use tokio::io::AsyncReadExt;
175175
use tokio::io::AsyncWrite;
176176
use tokio::io::AsyncWriteExt;
177177

178+
use miniz_oxide::DataFormat;
179+
use miniz_oxide::inflate::stream::InflateState;
180+
178181
pub use crate::close::CloseCode;
179182
pub use crate::error::WebSocketError;
180183
pub use crate::fragment::FragmentCollector;
@@ -208,6 +211,8 @@ pub(crate) struct ReadHalf {
208211
writev_threshold: usize,
209212
max_message_size: usize,
210213
buffer: BytesMut,
214+
215+
state: InflateState,
211216
}
212217

213218
#[cfg(feature = "unstable-split")]
@@ -364,6 +369,7 @@ pub struct WebSocket<S> {
364369
stream: S,
365370
write_half: WriteHalf,
366371
read_half: ReadHalf,
372+
367373
}
368374

369375
impl<'f, S> WebSocket<S> {
@@ -577,6 +583,8 @@ impl ReadHalf {
577583
pub fn after_handshake(role: Role) -> Self {
578584
let buffer = BytesMut::with_capacity(8192);
579585

586+
let state = InflateState::new(DataFormat::Raw);
587+
580588
Self {
581589
role,
582590
auto_apply_mask: true,
@@ -585,6 +593,7 @@ impl ReadHalf {
585593
writev_threshold: 1024,
586594
max_message_size: 64 << 20,
587595
buffer,
596+
state,
588597
}
589598
}
590599

@@ -610,6 +619,13 @@ impl ReadHalf {
610619
frame.unmask()
611620
};
612621

622+
if frame.compressed {
623+
frame = match frame.inflate(&mut self.state) {
624+
Ok(frame) => frame,
625+
Err(e) => return (Err(e), None),
626+
}
627+
}
628+
613629
match frame.opcode {
614630
OpCode::Close if self.auto_close => {
615631
match frame.payload.len() {
@@ -681,7 +697,11 @@ impl ReadHalf {
681697
let rsv2 = self.buffer[0] & 0b00100000 != 0;
682698
let rsv3 = self.buffer[0] & 0b00010000 != 0;
683699

684-
if rsv1 || rsv2 || rsv3 {
700+
let mut compressed = false;
701+
702+
if rsv1 && !rsv2 && !rsv3 {
703+
compressed = true;
704+
} else if rsv1 || rsv2 || rsv3 {
685705
return Err(WebSocketError::ReservedBitsNotZero);
686706
}
687707

@@ -744,7 +764,7 @@ impl ReadHalf {
744764

745765
// if we read too much it will stay in the buffer, for the next call to this method
746766
let payload = self.buffer.split_to(payload_len);
747-
let frame = Frame::new(fin, opcode, mask, Payload::Bytes(payload));
767+
let frame = Frame::new(fin, opcode, mask, Payload::Bytes(payload), compressed);
748768
Ok(frame)
749769
}
750770
}

0 commit comments

Comments
 (0)