rtmp_rs/server/
config.rs

1//! Server configuration
2
3use std::net::SocketAddr;
4use std::time::Duration;
5
6use crate::protocol::constants::*;
7
8/// Server configuration options
9#[derive(Debug, Clone)]
10pub struct ServerConfig {
11    /// Address to bind to
12    pub bind_addr: SocketAddr,
13
14    /// Maximum concurrent connections (0 = unlimited)
15    pub max_connections: usize,
16
17    /// Chunk size to negotiate with clients
18    pub chunk_size: u32,
19
20    /// Window acknowledgement size
21    pub window_ack_size: u32,
22
23    /// Peer bandwidth limit
24    pub peer_bandwidth: u32,
25
26    /// Connection timeout (handshake must complete within this time)
27    pub connection_timeout: Duration,
28
29    /// Idle timeout (disconnect if no data received)
30    pub idle_timeout: Duration,
31
32    /// Enable TCP_NODELAY (disable Nagle's algorithm)
33    pub tcp_nodelay: bool,
34
35    /// TCP receive buffer size (0 = OS default)
36    pub tcp_recv_buffer: usize,
37
38    /// TCP send buffer size (0 = OS default)
39    pub tcp_send_buffer: usize,
40
41    /// Application-level read buffer size
42    pub read_buffer_size: usize,
43
44    /// Application-level write buffer size
45    pub write_buffer_size: usize,
46
47    /// Enable GOP buffering for late-joiner support
48    pub gop_buffer_enabled: bool,
49
50    /// Maximum GOP buffer size in bytes
51    pub gop_buffer_max_size: usize,
52
53    /// Stats update interval
54    pub stats_interval: Duration,
55}
56
57impl Default for ServerConfig {
58    fn default() -> Self {
59        Self {
60            bind_addr: "0.0.0.0:1935".parse().unwrap(),
61            max_connections: 0, // Unlimited
62            chunk_size: RECOMMENDED_CHUNK_SIZE,
63            window_ack_size: DEFAULT_WINDOW_ACK_SIZE,
64            peer_bandwidth: DEFAULT_PEER_BANDWIDTH,
65            connection_timeout: Duration::from_secs(10),
66            idle_timeout: Duration::from_secs(60),
67            tcp_nodelay: true, // Important for low latency
68            tcp_recv_buffer: 0,
69            tcp_send_buffer: 0,
70            read_buffer_size: 64 * 1024, // 64KB
71            write_buffer_size: 64 * 1024,
72            gop_buffer_enabled: true,
73            gop_buffer_max_size: 4 * 1024 * 1024, // 4MB
74            stats_interval: Duration::from_secs(5),
75        }
76    }
77}
78
79impl ServerConfig {
80    /// Create a new config with custom bind address
81    pub fn with_addr(addr: SocketAddr) -> Self {
82        Self {
83            bind_addr: addr,
84            ..Default::default()
85        }
86    }
87
88    /// Set the bind address
89    pub fn bind(mut self, addr: SocketAddr) -> Self {
90        self.bind_addr = addr;
91        self
92    }
93
94    /// Set maximum connections
95    pub fn max_connections(mut self, max: usize) -> Self {
96        self.max_connections = max;
97        self
98    }
99
100    /// Set chunk size
101    pub fn chunk_size(mut self, size: u32) -> Self {
102        self.chunk_size = size.min(MAX_CHUNK_SIZE);
103        self
104    }
105
106    /// Disable GOP buffering
107    pub fn disable_gop_buffer(mut self) -> Self {
108        self.gop_buffer_enabled = false;
109        self
110    }
111
112    /// Set connection timeout
113    pub fn connection_timeout(mut self, timeout: Duration) -> Self {
114        self.connection_timeout = timeout;
115        self
116    }
117
118    /// Set idle timeout
119    pub fn idle_timeout(mut self, timeout: Duration) -> Self {
120        self.idle_timeout = timeout;
121        self
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128
129    #[test]
130    fn test_default_config() {
131        let config = ServerConfig::default();
132
133        assert_eq!(config.bind_addr.port(), 1935);
134        assert_eq!(config.max_connections, 0);
135        assert_eq!(config.chunk_size, RECOMMENDED_CHUNK_SIZE);
136        assert_eq!(config.window_ack_size, DEFAULT_WINDOW_ACK_SIZE);
137        assert_eq!(config.peer_bandwidth, DEFAULT_PEER_BANDWIDTH);
138        assert!(config.tcp_nodelay);
139        assert!(config.gop_buffer_enabled);
140    }
141
142    #[test]
143    fn test_with_addr() {
144        let addr: SocketAddr = "127.0.0.1:1936".parse().unwrap();
145        let config = ServerConfig::with_addr(addr);
146
147        assert_eq!(config.bind_addr.port(), 1936);
148    }
149
150    #[test]
151    fn test_builder_bind() {
152        let addr: SocketAddr = "0.0.0.0:8080".parse().unwrap();
153        let config = ServerConfig::default().bind(addr);
154
155        assert_eq!(config.bind_addr, addr);
156    }
157
158    #[test]
159    fn test_builder_max_connections() {
160        let config = ServerConfig::default().max_connections(100);
161
162        assert_eq!(config.max_connections, 100);
163    }
164
165    #[test]
166    fn test_builder_chunk_size() {
167        let config = ServerConfig::default().chunk_size(8192);
168
169        assert_eq!(config.chunk_size, 8192);
170    }
171
172    #[test]
173    fn test_builder_chunk_size_capped() {
174        // Chunk size should be capped at MAX_CHUNK_SIZE
175        let config = ServerConfig::default().chunk_size(u32::MAX);
176
177        assert_eq!(config.chunk_size, MAX_CHUNK_SIZE);
178    }
179
180    #[test]
181    fn test_builder_disable_gop_buffer() {
182        let config = ServerConfig::default().disable_gop_buffer();
183
184        assert!(!config.gop_buffer_enabled);
185    }
186
187    #[test]
188    fn test_builder_connection_timeout() {
189        let config = ServerConfig::default().connection_timeout(Duration::from_secs(30));
190
191        assert_eq!(config.connection_timeout, Duration::from_secs(30));
192    }
193
194    #[test]
195    fn test_builder_idle_timeout() {
196        let config = ServerConfig::default().idle_timeout(Duration::from_secs(120));
197
198        assert_eq!(config.idle_timeout, Duration::from_secs(120));
199    }
200
201    #[test]
202    fn test_builder_chaining() {
203        let addr: SocketAddr = "127.0.0.1:1935".parse().unwrap();
204        let config = ServerConfig::default()
205            .bind(addr)
206            .max_connections(50)
207            .chunk_size(4096)
208            .connection_timeout(Duration::from_secs(5))
209            .idle_timeout(Duration::from_secs(30))
210            .disable_gop_buffer();
211
212        assert_eq!(config.bind_addr, addr);
213        assert_eq!(config.max_connections, 50);
214        assert_eq!(config.chunk_size, 4096);
215        assert_eq!(config.connection_timeout, Duration::from_secs(5));
216        assert_eq!(config.idle_timeout, Duration::from_secs(30));
217        assert!(!config.gop_buffer_enabled);
218    }
219}