Skip to content

Commit e7e6d55

Browse files
committed
updated README
1 parent 88fb308 commit e7e6d55

File tree

2 files changed

+307
-7
lines changed

2 files changed

+307
-7
lines changed

CHANGELOG.md

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,196 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [Unreleased]
9+
10+
### Added
11+
12+
#### Streaming API
13+
14+
- **New `Streaming` type**: Low-level WebSocket API with manual fragment control
15+
- Provides direct access to WebSocket frames without automatic reassembly
16+
- Enables memory-efficient streaming of large messages
17+
- Supports streaming compression with partial flushes
18+
- Convert from `WebSocket` using `.into_streaming()`
19+
- See documentation for `Streaming` type for usage examples
20+
21+
#### Compression Enhancements
22+
23+
- **Streaming compression support**: Compress data incrementally without buffering entire messages
24+
- New `compress_streaming()` method on `Compressor` for partial flushes
25+
- Enables real-time compression of large payloads
26+
- Reduces memory usage for large messages
27+
28+
- **No-context-takeover support for streaming**: Reset compression context after each message in streaming mode
29+
- Ensures consistent memory usage across long-lived connections
30+
- Available for both `Compressor` and `Decompressor`
31+
32+
#### Configuration Options
33+
34+
- **`with_max_fragment_size(size)`**: Configure automatic fragmentation of outgoing messages
35+
- Messages exceeding this size are automatically split into fragments
36+
- Useful for controlling memory usage and latency
37+
38+
- **`with_backpressure_boundary(size)`**: Set backpressure boundary for write buffer
39+
- Prevents unbounded memory growth when sending large amounts of data
40+
- Enables flow control for high-throughput applications
41+
42+
- **Fragment timeout configuration**: Control timeout for incomplete fragmented message assembly
43+
- Protects against partial messages that never complete
44+
- Configurable via `with_fragment_timeout(duration)`
45+
46+
### Changed
47+
48+
- **Improved compression handling**: Compression now only applies to complete messages (FIN frames)
49+
- Fragmented messages handle compression at the message level, not per-fragment
50+
- More efficient and RFC-compliant behavior
51+
52+
- **WriteHalf simplification**: Removed role handling and compression from WriteHalf
53+
- Compression is now handled at the WebSocket layer
54+
- Cleaner separation of concerns
55+
56+
### Fixed
57+
58+
- **Compression context management**: Fixed issues with compression state across multiple messages
59+
- **Autobahn test coverage**: Expanded test cases for better protocol compliance verification
60+
61+
## [0.3.1]
62+
63+
### Changed
64+
65+
- **Simplified WriteHalf**: Removed role parameter from WriteHalf implementation
66+
- **Compression handling moved to WebSocket layer**: Compression now only applies to FIN text/binary frames
67+
- Fragment-level compression is managed at the message level
68+
- More efficient and correct implementation
69+
70+
### Fixed
71+
72+
- **Compression state management**: Fixed compression context handling for complete messages
73+
74+
### Documentation
75+
76+
- Updated upgrade guide with compression best practices
77+
- Enhanced README with clearer compression examples
78+
79+
## [0.3.0]
80+
81+
### Added
82+
83+
#### Generic Stream Support
84+
85+
- **Generic `AsyncRead + AsyncWrite` support**: WebSocket now accepts any stream implementing tokio's async traits
86+
- Enables integration with other runtimes via adapters
87+
- Better flexibility for custom transport layers
88+
- Examples: `client_smol.rs`, `echo_server_smol.rs`
89+
90+
#### New Examples
91+
92+
- `examples/client_smol.rs`: Using yawc with the smol runtime
93+
- `examples/echo_server_smol.rs`: Echo server with smol runtime
94+
- `examples/streaming.rs`: Streaming large payloads efficiently
95+
- `examples/auth_client.rs`: Authentication flow with custom headers
96+
97+
#### Configuration & Features
98+
99+
- **Fragment timeout support**: Configurable timeout for incomplete fragmented messages
100+
- New error: `WebSocketError::FragmentTimeout`
101+
- Configure via `Options::with_fragment_timeout()`
102+
103+
- **TCP_NODELAY support**: Disable Nagle's algorithm for lower latency
104+
- Configure via `Options::with_no_delay()`
105+
- Improves latency for small, frequent messages
106+
107+
- **Custom DNS resolution**: Support for custom DNS resolvers and TCP address override
108+
- Useful for testing and custom networking scenarios
109+
- See `examples/custom_dns.rs`
110+
111+
#### Compression Improvements
112+
113+
- **Improved compression context handling**: Better management of deflate compression contexts
114+
- Fixed edge cases in compression negotiation
115+
- Better handling of window bits with and without values
116+
- Improved zlib support with `zlib` feature flag
117+
118+
- **Compression test suite**: Extensive tests for compression edge cases
119+
- Tests for context takeover behavior
120+
- Fragmented compression tests
121+
- Stress tests with various data patterns
122+
123+
### Changed
124+
125+
#### Architecture Improvements
126+
127+
- **Fragment handling moved from ReadHalf to WebSocket**: More intuitive and RFC-compliant
128+
- Transparent fragmentation at the WebSocket level
129+
- Better separation of concerns
130+
- Improved testability
131+
132+
- **Simplified codec layer**: More efficient frame encoding/decoding
133+
- Proper role-based masking enforcement
134+
- Better buffer management
135+
- Reduced allocations
136+
137+
- **WebSocket layering improvements**: Cleaner separation between layers
138+
- Codec → ReadHalf → WebSocket hierarchy
139+
- Each layer has well-defined responsibilities
140+
- Better documentation of data flow
141+
142+
#### Code Quality
143+
144+
- **Removed FrameView dependency**: Unified on `Frame` type
145+
- Simpler API surface
146+
- Better performance
147+
- Less confusion about frame ownership
148+
149+
- **Buffer optimizations**: Reduced buffer allocations and copies
150+
- Payload stored as `Bytes` instead of `BytesMut`
151+
- Better use of zero-copy operations
152+
- Improved memory efficiency
153+
154+
#### WASM Improvements
155+
156+
- **Binary mode support for WASM**: Full support for binary WebSocket messages in WebAssembly
157+
- Previously only text mode was supported
158+
- Feature parity with native targets
159+
160+
### Breaking Changes
161+
162+
- **Reqwest updated to 0.13**: If using the `reqwest` feature, update your dependency
163+
- **Removed `json` feature flag**: Add `serde_json` directly to dependencies if needed
164+
165+
### Fixed
166+
167+
- **Close frame handling**: Better validation and propagation of close frames
168+
- Distinguish between empty and malformed close reasons
169+
- Proper error propagation in `poll_next_frame`
170+
- Close frames are now exposed to users for custom handling
171+
172+
- **Compression bugs**: Fixed several issues with permessage-deflate
173+
- Fixed deflate stream suffix handling
174+
- Improved sync flush behavior
175+
- Better context takeover management
176+
177+
- **Windows compatibility**: Various fixes for Windows platform support
178+
- **WASM test compatibility**: Tests properly skip on WASM targets
179+
- **Masking bugs**: Fixed role-based frame masking for client/server
180+
181+
### Development
182+
183+
- **Improved Autobahn testing**: Better test scripts and coverage
184+
- More comprehensive fuzzing
185+
- Improved test reporting
186+
- Environment configuration support
187+
188+
- **Better benchmarking infrastructure**: Enhanced performance testing
189+
- See `benches/` directory for comparative benchmarks
190+
191+
### Documentation
192+
193+
- Enhanced inline documentation throughout the codebase
194+
- Better examples covering common use cases
195+
- Improved architecture documentation in README
196+
- Migration guides updated
197+
8198
## [0.2.0]
9199

10200
### Breaking Changes

README.md

Lines changed: 117 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,43 @@ Yet another websocket crate. But a fast, secure, and RFC-compliant WebSocket imp
1111

1212
- **Full RFC 6455 Compliance**: Complete implementation of the WebSocket protocol
1313
- **Secure by Default**: Built-in TLS support with `rustls`
14-
- **Advanced Compression**: Support for permessage-deflate (RFC 7692)
14+
- **Advanced Compression**: Support for permessage-deflate (RFC 7692) with streaming compression and context takeover control
1515
- **Zero-Copy Design**: Efficient frame processing with minimal allocations
16-
- **Automatic Frame Management**: Handles control frames and fragmentation
16+
- **Streaming API**: Low-level API for manual fragment control, memory-efficient processing of large messages, and fine-grained compression control
17+
- **Automatic Frame Management**: Handles control frames and fragmentation automatically, or use `Streaming` for manual control
18+
- **Flow Control**: Configurable backpressure boundaries and automatic fragmentation for large messages
1719
- **Autobahn Test Suite**: Passes all test cases for both client and server modes
1820
- **WebAssembly Support**: Works seamlessly in WASM environments for browser-based applications (both text and binary modes supported)
1921

2022
## About compression
2123

22-
yawc supports websocket compression through the [Options](https://docs.rs/yawc/latest/yawc/struct.Options.html) struct.
23-
You can use `Options.with_compression_level(CompressLevel::fast())` in order to configure compression.
24+
yawc supports WebSocket compression through the [Options](https://docs.rs/yawc/latest/yawc/struct.Options.html) struct with the permessage-deflate extension (RFC 7692).
25+
26+
### Basic Compression
2427

2528
```rust
2629
let mut client = WebSocket::connect("wss://my-websocket-server.com".parse().unwrap())
2730
.with_options(Options::default().with_compression_level(CompressionLevel::fast()))
2831
.await;
2932
```
3033

31-
The `zlib` feature is NOT mandatory to enable compression. `zlib` is configured as a feature for the [window](https://docs.rs/yawc/latest/yawc/struct.Options.html#method.with_client_max_window_bits) parameters.
32-
By default yawc uses [flate2](https://docs.rs/flate2/) with the miniz_oxide backend. An implementation of miniz using Rust.
34+
### Advanced Compression Features
35+
36+
- **Streaming compression**: Compress large messages incrementally without buffering (available via `Streaming` API)
37+
- **Context takeover control**: Reset compression state between messages for consistent memory usage
38+
- **Configurable compression levels**: Balance between compression ratio and CPU usage
39+
- **Window size control**: Fine-tune memory vs compression tradeoff (requires `zlib` feature)
40+
41+
```rust
42+
// Example: Memory-optimized compression for long-lived connections
43+
let options = Options::default()
44+
.with_compression_level(CompressionLevel::fast())
45+
.server_no_context_takeover() // Reset context after each message
46+
.client_no_context_takeover(); // Prevent client-side memory growth
47+
```
48+
49+
The `zlib` feature is NOT mandatory to enable compression. `zlib` is only required for the [window bits](https://docs.rs/yawc/latest/yawc/struct.Options.html#method.with_client_max_window_bits) configuration parameters.
50+
By default yawc uses [flate2](https://docs.rs/flate2/) with the miniz_oxide backend - a pure Rust implementation of deflate.
3351

3452
## Upgrading or Migrating?
3553

@@ -213,6 +231,48 @@ futures = { version = "0.3", default-features = false, features = ["std"] }
213231

214232
## Advanced Features
215233

234+
### Streaming API
235+
236+
For advanced use cases requiring manual control over frame fragmentation, yawc provides a low-level `Streaming` API:
237+
238+
```rust
239+
use yawc::{WebSocket, Frame, OpCode};
240+
use futures::{SinkExt, StreamExt};
241+
242+
// Convert WebSocket to Streaming for manual fragment control
243+
let ws = WebSocket::connect("wss://example.com".parse()?).await?;
244+
let mut streaming = ws.into_streaming();
245+
246+
// Send a large message as multiple fragments manually
247+
streaming.send(Frame::text("First part").with_fin(false)).await?;
248+
streaming.send(Frame::continuation(" second part").with_fin(false)).await?;
249+
streaming.send(Frame::continuation(" final part")).await?;
250+
251+
// Receive frames without automatic reassembly
252+
while let Some(frame) = streaming.next().await {
253+
match frame.opcode() {
254+
OpCode::Text => println!("Text fragment: FIN={}", frame.is_fin()),
255+
OpCode::Continuation => println!("Continuation: FIN={}", frame.is_fin()),
256+
_ => {}
257+
}
258+
}
259+
```
260+
261+
**When to use `Streaming`:**
262+
263+
- Streaming large files directly from/to disk without buffering in memory
264+
- Implementing custom fragmentation strategies for specific protocols
265+
- Processing data incrementally as fragments arrive for real-time applications
266+
- Fine-grained control over compression boundaries and frame timing
267+
268+
**Key differences from `WebSocket`:**
269+
270+
- No automatic fragment reassembly - you receive individual frames
271+
- No automatic fragmentation - you control when messages are split
272+
- Supports streaming compression with partial flushes
273+
- Lower memory usage for large messages
274+
- More control, but requires understanding of WebSocket fragmentation protocol
275+
216276
### Compression Control
217277

218278
Fine-tune compression settings for optimal performance:
@@ -224,12 +284,62 @@ let ws = WebSocket::connect("wss://example.com".parse()?)
224284
.with_options(
225285
Options::default()
226286
.with_compression_level(CompressionLevel::default())
227-
.server_no_context_takeover() // Optimize memory usage
287+
.server_no_context_takeover() // Reset compression context after each message
288+
.client_no_context_takeover() // Optimize memory for client side
228289
.with_client_max_window_bits(11) // Control compression window (requires zlib feature)
229290
)
230291
.await?;
231292
```
232293

294+
**Context Takeover Options:**
295+
296+
The `no_context_takeover` options control how compression state is managed between messages:
297+
298+
- **With context takeover (default)**: The compression dictionary is maintained across messages, providing better compression ratios for similar data but using more memory over time.
299+
300+
- **Without context takeover**: The compression dictionary is reset after each message, trading compression efficiency for consistent memory usage. Ideal for:
301+
- Long-lived connections where memory growth is a concern
302+
- Memory-constrained environments (mobile devices, embedded systems)
303+
- Applications with diverse message content where dictionary reuse provides little benefit
304+
305+
```rust
306+
// Example: Memory-optimized compression for long-lived connections
307+
let options = Options::default()
308+
.with_compression_level(CompressionLevel::fast())
309+
.server_no_context_takeover()
310+
.client_no_context_takeover();
311+
```
312+
313+
### Automatic Fragmentation and Flow Control
314+
315+
Configure automatic fragmentation and backpressure for large messages:
316+
317+
```rust
318+
use yawc::{WebSocket, Options};
319+
use std::time::Duration;
320+
321+
let ws = WebSocket::connect("wss://example.com".parse()?)
322+
.with_options(
323+
Options::default()
324+
.with_max_fragment_size(64 * 1024) // Auto-fragment messages > 64 KiB
325+
.with_backpressure_boundary(128 * 1024) // Apply backpressure at 128 KiB
326+
.with_fragment_timeout(Duration::from_secs(30)) // Timeout incomplete fragments
327+
)
328+
.await?;
329+
```
330+
331+
**Fragmentation options:**
332+
333+
- `with_max_fragment_size()`: Automatically split large outgoing messages into fragments
334+
- `with_fragment_timeout()`: Protect against incomplete fragmented messages that never complete
335+
- `with_backpressure_boundary()`: Control memory usage by applying backpressure when write buffer grows
336+
337+
These options are particularly useful for:
338+
339+
- Handling large file uploads/downloads
340+
- Streaming real-time data with bounded memory
341+
- Preventing memory exhaustion from slow consumers
342+
233343
### Split Streams
234344

235345
Split the WebSocket for independent reading and writing:

0 commit comments

Comments
 (0)