rustapi_ws/
compression.rs

1//! WebSocket compression configuration
2//!
3//! this module provides configuration for WebSocket compression (per-message deflate).
4
5/// Configuration for WebSocket compression
6#[derive(Debug, Clone, Copy)]
7pub struct WsCompressionConfig {
8    /// Minimum size of message to compress (in bytes)
9    pub min_size: usize,
10    /// Server window bits (9-15)
11    pub window_bits: u8,
12    /// Client window bits (9-15)
13    pub client_window_bits: u8,
14}
15
16impl Default for WsCompressionConfig {
17    fn default() -> Self {
18        Self {
19            min_size: 256,
20            window_bits: 15,
21            client_window_bits: 15,
22        }
23    }
24}
25
26impl WsCompressionConfig {
27    /// Create a new compression config with default values
28    pub fn new() -> Self {
29        Self::default()
30    }
31
32    /// Set minimum message size to compress
33    pub fn min_size(mut self, size: usize) -> Self {
34        self.min_size = size;
35        self
36    }
37
38    /// Set server window bits (9-15)
39    pub fn window_bits(mut self, bits: u8) -> Self {
40        self.window_bits = bits.clamp(9, 15);
41        self
42    }
43
44    /// Set client window bits (9-15)
45    pub fn client_window_bits(mut self, bits: u8) -> Self {
46        self.client_window_bits = bits.clamp(9, 15);
47        self
48    }
49}
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54    use crate::WebSocketUpgrade;
55    use proptest::prelude::*;
56
57    proptest! {
58        #[test]
59        fn test_compression_negotiation(
60            min_size in 0usize..10000,
61            window_bits in 9u8..15,
62            client_window_bits in 9u8..15,
63            client_supports_compression in proptest::bool::ANY,
64        ) {
65            let config = WsCompressionConfig::new()
66                .min_size(min_size)
67                .window_bits(window_bits)
68                .client_window_bits(client_window_bits);
69
70            let client_extensions = if client_supports_compression {
71                Some("permessage-deflate; client_max_window_bits".to_string())
72            } else {
73                None
74            };
75
76            let upgrade = WebSocketUpgrade::new(
77                "dGhlIHNhbXBsZSBub25jZQ==".to_string(),
78                client_extensions,
79                None, // No OnUpgrade future in tests
80            )
81            .compress(config);
82
83            let response = upgrade.into_response_inner();
84            let ext_header = response.headers().get("Sec-WebSocket-Extensions");
85
86            if client_supports_compression {
87                assert!(ext_header.is_some(), "Header missing when client supports compression");
88                let header_str = ext_header.unwrap().to_str().unwrap();
89                assert!(header_str.contains("permessage-deflate"));
90
91                if window_bits < 15 {
92                    assert!(header_str.contains(&format!("server_max_window_bits={}", window_bits)));
93                }
94                if client_window_bits < 15 {
95                    assert!(header_str.contains(&format!("client_max_window_bits={}", client_window_bits)));
96                }
97            } else {
98                assert!(ext_header.is_none());
99            }
100        }
101    }
102}