ntex_io/cfg.rs
1//! I/O buffer, timeout, and frame-rate configuration.
2
3use ntex_bytes::BytePageSize;
4use ntex_service::cfg::{CfgContext, Configuration};
5use ntex_util::{time::Millis, time::Seconds};
6
7#[derive(Debug)]
8/// Shared configuration for an [`crate::Io`] stream.
9pub struct IoConfig {
10 connect_timeout: Millis,
11 keepalive_timeout: Seconds,
12 shutdown_timeout: Seconds,
13 frame_read_rate: Option<FrameReadRate>,
14 write_timeout: Seconds,
15
16 // read side configuration
17 read_size_min: BytePageSize,
18 read_size_max: BytePageSize,
19 read_backpressure: usize,
20
21 // write side configuration
22 write_size: BytePageSize,
23 write_backpressure: usize,
24 write_buf_threshold: usize,
25
26 // shared config
27 pub(crate) config: CfgContext,
28}
29
30impl Default for IoConfig {
31 fn default() -> Self {
32 IoConfig::new()
33 }
34}
35
36impl Configuration for IoConfig {
37 const NAME: &str = "IO Configuration";
38
39 fn ctx(&self) -> &CfgContext {
40 &self.config
41 }
42
43 fn set_ctx(&mut self, ctx: CfgContext) {
44 self.config = ctx;
45 }
46}
47
48/// Minimum read rate required while decoding one frame.
49#[derive(Copy, Clone, Debug)]
50pub struct FrameReadRate {
51 /// Initial read timeout.
52 pub timeout: Seconds,
53 /// Maximum cumulative timeout for the frame.
54 pub max_timeout: Seconds,
55 /// Byte-progress threshold that must be exceeded to extend the deadline.
56 ///
57 /// Progress must be strictly greater than this value for another `timeout`
58 /// period to be granted.
59 pub rate: u32,
60}
61
62impl IoConfig {
63 #[inline]
64 #[must_use]
65 /// Creates an I/O configuration with default settings.
66 pub fn new() -> IoConfig {
67 IoConfig {
68 config: CfgContext::default(),
69 connect_timeout: Millis::ZERO,
70 keepalive_timeout: Seconds(0),
71 shutdown_timeout: Seconds(1),
72 frame_read_rate: None,
73
74 read_size_min: BytePageSize::Size4,
75 read_size_max: BytePageSize::Size64,
76 read_backpressure: BytePageSize::Size32.capacity(),
77
78 write_timeout: Seconds(0),
79 write_size: BytePageSize::Size16,
80 write_backpressure: BytePageSize::Size16.capacity(),
81 write_buf_threshold: BytePageSize::Size16.half_capacity(),
82 }
83 }
84
85 #[inline]
86 /// Returns the shared configuration tag.
87 pub fn tag(&self) -> &str {
88 self.config.tag()
89 }
90
91 #[inline]
92 /// Returns the connection timeout.
93 pub fn connect_timeout(&self) -> Millis {
94 self.connect_timeout
95 }
96
97 #[inline]
98 /// Returns the keep-alive timeout.
99 pub fn keepalive_timeout(&self) -> Seconds {
100 self.keepalive_timeout
101 }
102
103 #[inline]
104 /// Returns the graceful shutdown timeout.
105 pub fn shutdown_timeout(&self) -> Seconds {
106 self.shutdown_timeout
107 }
108
109 #[inline]
110 /// Returns the frame read-rate configuration.
111 pub fn frame_read_rate(&self) -> Option<&FrameReadRate> {
112 self.frame_read_rate.as_ref()
113 }
114
115 #[inline]
116 /// Returns the smallest page size of read buffers.
117 ///
118 /// Connections start reading into pages of this size, see
119 /// [`set_read_size`](Self::set_read_size).
120 pub fn read_size_min(&self) -> BytePageSize {
121 self.read_size_min
122 }
123
124 #[inline]
125 /// Returns the largest page size of read buffers.
126 ///
127 /// The read page size of a connection adapts to its input up to this
128 /// size, see [`set_read_size`](Self::set_read_size).
129 pub fn read_size_max(&self) -> BytePageSize {
130 self.read_size_max
131 }
132
133 #[inline]
134 /// Returns the read backpressure high watermark.
135 ///
136 /// Read backpressure is enabled once buffered input reaches it and
137 /// released once it falls to half of it, see
138 /// [`set_read_backpressure`](Self::set_read_backpressure).
139 pub fn read_backpressure(&self) -> usize {
140 self.read_backpressure
141 }
142
143 #[inline]
144 /// Returns the write backpressure timeout.
145 ///
146 /// A zero value means the timeout is disabled.
147 pub fn write_timeout(&self) -> Seconds {
148 self.write_timeout
149 }
150
151 #[inline]
152 /// Returns the write backpressure high watermark.
153 ///
154 /// Write backpressure is released once outstanding output falls to half of
155 /// it, see [`set_write_backpressure`](Self::set_write_backpressure).
156 pub fn write_backpressure(&self) -> usize {
157 self.write_backpressure
158 }
159
160 #[inline]
161 /// Buffered input size that releases read backpressure.
162 pub(crate) fn read_half(&self) -> usize {
163 self.read_backpressure >> 1
164 }
165
166 #[inline]
167 /// Outstanding output size that releases write backpressure.
168 pub(crate) fn write_half(&self) -> usize {
169 self.write_backpressure >> 1
170 }
171
172 #[inline]
173 /// Returns the write-buffer page size.
174 pub fn write_size(&self) -> BytePageSize {
175 self.write_size
176 }
177
178 #[inline]
179 /// Returns the buffered write size that triggers an earlier send.
180 pub fn write_buf_threshold(&self) -> usize {
181 self.write_buf_threshold
182 }
183
184 /// Sets the connection timeout.
185 ///
186 /// A zero duration disables the timeout. It is disabled by default.
187 #[must_use]
188 pub fn set_connect_timeout<T: Into<Millis>>(mut self, timeout: T) -> Self {
189 self.connect_timeout = timeout.into();
190 self
191 }
192
193 /// Sets the keep-alive timeout.
194 ///
195 /// The dispatcher runs the timer only while the connection is idle: no
196 /// input is buffered, no partial frame is being read, and no decoded
197 /// frames are being handled. It starts when the dispatcher enters this
198 /// idle state, including after all decoded frames have been handled.
199 /// Partial frames are bounded by
200 /// [frame read-rate](Self::set_frame_read_rate) limits instead, and write
201 /// backpressure by the [write timeout](Self::set_write_timeout).
202 ///
203 /// A zero duration disables the timeout. It is disabled by default.
204 #[must_use]
205 pub fn set_keepalive_timeout<T: Into<Seconds>>(mut self, timeout: T) -> Self {
206 self.keepalive_timeout = timeout.into();
207 self
208 }
209
210 /// Sets the graceful shutdown timeout.
211 ///
212 /// A graceful shutdown runs in two phases, and this single timeout bounds
213 /// them together rather than applying to each one:
214 ///
215 /// 1. **Filter shutdown.** Both directions stay open, so a filter can emit
216 /// its closing data and still read the peer's. A TLS filter sends its
217 /// `close_notify` here, and a WebSocket filter its close frame.
218 /// 2. **Transport shutdown.** The remaining output is drained to the peer.
219 /// The read side is paused, and whatever the peer still sent is
220 /// discarded, then the connection is closed.
221 ///
222 /// The deadline is armed when the first phase begins and is not restarted
223 /// for the second, so a filter that shuts down slowly leaves less time to
224 /// drain. Expiry in the first phase moves on to the second rather than
225 /// giving up; only expiry in the second terminates the connection, and
226 /// output that has not reached the transport is then lost. Either way
227 /// [`crate::Io::shutdown`] reports a timed-out error once the transport
228 /// has stopped.
229 ///
230 /// The timeout also applies when no application output is pending: a
231 /// filter may still be waiting for the peer to complete its shutdown
232 /// exchange. If both phases can finish immediately, the deadline has no
233 /// observable effect.
234 ///
235 /// The default is one second.
236 ///
237 /// # Panics
238 ///
239 /// Panics if `timeout` is zero. Without a deadline, a peer that never
240 /// completes the exchange, or never reads the remaining output, could
241 /// hold the connection open forever.
242 #[must_use]
243 pub fn set_shutdown_timeout<T: Into<Seconds>>(mut self, timeout: T) -> Self {
244 let timeout = timeout.into();
245 assert!(
246 timeout.non_zero(),
247 "shutdown timeout must be greater than zero"
248 );
249 self.shutdown_timeout = timeout;
250 self
251 }
252
253 /// Sets read-rate parameters for a single decoded frame.
254 ///
255 /// Rate tracking starts when a new connection arrives, for its first
256 /// frame, and later whenever a decoder returns no complete item after
257 /// receiving partial frame data, whether the data is left in the read
258 /// buffer or consumed into the decoder's own state. The dispatcher then
259 /// allows one `timeout` period for additional data to arrive.
260 ///
261 /// When that period expires, the dispatcher compares the bytes received
262 /// for the frame since the previous check with `rate`:
263 ///
264 /// - If the progress is greater than `rate`, the deadline is extended by
265 /// another `timeout` period.
266 /// - If the progress is at most `rate`, frame decoding fails with a read
267 /// timeout.
268 /// - Completing the frame clears the timer and resets rate tracking for the
269 /// next frame.
270 ///
271 /// `max_timeout` limits the cumulative time allowed for one frame. A zero
272 /// value permits an unlimited number of extensions while the required rate
273 /// is maintained. A non-zero value is enforced in whole `timeout` periods,
274 /// so the effective limit is rounded up to a multiple of `timeout` and is
275 /// never shorter than the initial period.
276 ///
277 /// A zero `timeout` disables frame read-rate enforcement and ignores
278 /// `max_timeout` and `rate`. With a non-zero timeout and `rate` set to zero,
279 /// any positive byte progress permits another period.
280 ///
281 /// A new connection must therefore deliver its first frame within these
282 /// limits. After a frame has been decoded, idle connections with no
283 /// partial frame are governed separately by
284 /// [`set_keepalive_timeout`](Self::set_keepalive_timeout).
285 ///
286 /// The timer is stopped while write backpressure is active, when frames
287 /// are not decoded, and a new period starts once decoding resumes. While
288 /// the service is not ready the timer is stopped as well, and tracking
289 /// restarts with a fresh period and `max_timeout` budget once the service
290 /// is ready.
291 ///
292 /// Frame read-rate enforcement is disabled by default.
293 #[must_use]
294 pub fn set_frame_read_rate(
295 mut self,
296 timeout: Seconds,
297 max_timeout: Seconds,
298 rate: u32,
299 ) -> Self {
300 self.frame_read_rate = if timeout.is_zero() {
301 None
302 } else {
303 Some(FrameReadRate {
304 timeout,
305 max_timeout,
306 rate,
307 })
308 };
309 self
310 }
311
312 /// Sets the write backpressure timeout.
313 ///
314 /// Write backpressure is enabled when outstanding output reaches the
315 /// [write buffer](Self::set_write_backpressure) high watermark and disabled once the
316 /// peer has accepted enough of it. The timeout covers that whole period:
317 /// if backpressure is still enabled when it expires, the dispatcher stops
318 /// with a write timeout. Each backpressure period starts a fresh timeout,
319 /// however much the peer read during the previous one. Without a write
320 /// timeout, a peer that stops reading during backpressure can hold the
321 /// connection open indefinitely.
322 ///
323 /// The timeout does not apply once backpressure is disabled, even though
324 /// output is still outstanding. A peer that stops reading at that point
325 /// can leave up to half of the high watermark unwritten; only the
326 /// [keep-alive timeout](Self::set_keepalive_timeout), when enabled, bounds
327 /// such a connection until it is shut down.
328 ///
329 /// Reads paused because output produced by reading, for example replies
330 /// to peer pings, has not drained are not covered either. While the
331 /// dispatcher is idle, the keep-alive timeout bounds them. Application
332 /// output written during such a pause enables write backpressure.
333 ///
334 /// Outside the dispatcher, the timeout also bounds each wait for output in
335 /// [`Io::send`](crate::Io::send), [`Io::flush`](crate::Io::flush) and
336 /// [`IoRef::write_ready`](crate::IoRef::write_ready). A wait that does not
337 /// complete in time fails with [`io::ErrorKind::TimedOut`](std::io::ErrorKind::TimedOut),
338 /// and the connection is left open for the caller to close. The polling
339 /// methods, such as [`Io::poll_flush`](crate::Io::poll_flush), are not
340 /// bounded.
341 ///
342 /// A zero duration disables the timeout. It is disabled by default, so
343 /// a peer that does not read can pin the connection and its buffered
344 /// output. Servers that accept untrusted peers should set it.
345 #[must_use]
346 pub fn set_write_timeout(mut self, timeout: Seconds) -> Self {
347 self.write_timeout = timeout;
348 self
349 }
350
351 /// Sets the range of read-buffer page sizes.
352 ///
353 /// Each connection reads into pages of its own page size, which starts at
354 /// `min` and adapts to the connection's input between `min` and `max`.
355 /// Input that arrives in batches larger than the page capacity, such as
356 /// streamed bodies or large frames, grows the page size of the next read
357 /// buffers to fit a batch, so it is read with fewer and larger transport
358 /// reads. After several batches in a row that would fit in half of the
359 /// next smaller page size, the page size shrinks by one step. Connections
360 /// with small messages keep small pages, which also bounds the memory a
361 /// partial frame or a split-off frame holds.
362 ///
363 /// Within a batch a buffer grows with [`BytesMut::reserve_more`](ntex_bytes::BytesMut::reserve_more) once less
364 /// than [`BytePageSize::low`] of its page size remains: the data is
365 /// compacted within its page when that leaves room for half a page,
366 /// otherwise the buffer moves to the next page size.
367 ///
368 /// The page size does not depend on the read backpressure watermark, see
369 /// [`set_read_backpressure`](Self::set_read_backpressure). A single read
370 /// can fill a page larger than the watermark, reads are paused only after
371 /// the buffered input reaches it.
372 ///
373 /// Read buffers come from the per-thread page cache of `ntex-bytes`,
374 /// shared with write buffers and all configurations, see
375 /// [`ntex_bytes::set_page_cache_size`]. A buffer returns to the cache of
376 /// the thread that drops its last reference. Buffers grown beyond the
377 /// largest page size are freed. A connection holding
378 /// unconsumed input, such as the start of a frame that has not fully
379 /// arrived, keeps its whole read buffer, so each such connection uses at
380 /// least one page until the rest arrives. Read-rate timeouts,
381 /// see [`set_frame_read_rate`](Self::set_frame_read_rate), bound how long
382 /// a slow peer can hold it.
383 ///
384 /// Frames that a codec splits off the read buffer, such as `Bytes`
385 /// payloads, share its allocation. A frame kept alive keeps the whole
386 /// read buffer allocated, it returns to the cache only once the last such
387 /// frame is dropped, so retaining many small frames can use far more
388 /// memory than their size.
389 /// Copy long-lived frames or call [`Bytes::trimdown`](ntex_bytes::Bytes::trimdown)
390 /// on them to release the rest of the buffer.
391 ///
392 /// Set `min` and `max` to the same size to read into pages of a fixed
393 /// size. The default range is 4 KiB to 64 KiB.
394 ///
395 /// # Panics
396 ///
397 /// Panics if `min` or `max` is [`BytePageSize::Unset`], or `min` is
398 /// larger than `max`.
399 #[must_use]
400 pub fn set_read_size(mut self, min: BytePageSize, max: BytePageSize) -> Self {
401 assert!(
402 min != BytePageSize::Unset && max != BytePageSize::Unset,
403 "read buffer page size must be set"
404 );
405 assert!(
406 min.capacity() <= max.capacity(),
407 "read buffer min page size must not be larger than max"
408 );
409 self.read_size_min = min;
410 self.read_size_max = max;
411 self
412 }
413
414 /// Sets the read backpressure watermark.
415 ///
416 /// Read backpressure is enabled when the application-facing read buffer
417 /// reaches `size` bytes and released once it falls to half of it. It does
418 /// not affect the read page size, see [`set_read_size`](Self::set_read_size).
419 ///
420 /// The default watermark is the capacity of a 32 KiB page.
421 ///
422 /// # Panics
423 ///
424 /// Panics if `size` is zero.
425 #[must_use]
426 pub fn set_read_backpressure(mut self, size: usize) -> Self {
427 assert!(size > 0, "read backpressure must be greater than zero");
428 self.read_backpressure = size;
429 self
430 }
431
432 /// Sets the write-buffer page size.
433 ///
434 /// Write buffers are represented as a sequence of reusable byte pages.
435 /// `size` selects the capacity category used when those buffers allocate
436 /// new internal pages, including the intermediate write buffers created
437 /// between filter layers. [`BytePages`](ntex_bytes::BytePages) may also
438 /// contain externally supplied [`BytePage`](ntex_bytes::BytePage),
439 /// [`Bytes`](ntex_bytes::Bytes), or `Vec<u8>` segments; this setting does
440 /// not resize or copy those segments.
441 ///
442 /// Smaller pages reduce unused capacity for connections that usually
443 /// produce small writes. Larger pages can reduce allocation and page-list
444 /// overhead for connections that regularly buffer larger writes. This
445 /// setting does not limit the total amount of buffered data or determine
446 /// the size of individual transport write operations.
447 ///
448 /// Changing the page size on an active connection through
449 /// [`Io::set_config`](crate::Io::set_config) updates the allocation
450 /// category for future pages in every existing write buffer. Pages that
451 /// have already been allocated retain their current capacity. Filter
452 /// layers added later use the new page size.
453 ///
454 /// The page size is independent of the eager-write threshold and write
455 /// backpressure watermarks. Changing it does not update values configured
456 /// by [`set_write_buf_threshold`](Self::set_write_buf_threshold) or
457 /// [`set_write_backpressure`](Self::set_write_backpressure).
458 ///
459 /// The default page size is 16 KiB.
460 ///
461 /// # Panics
462 ///
463 /// Panics if `size` is [`BytePageSize::Unset`].
464 #[must_use]
465 pub fn set_write_size(mut self, size: BytePageSize) -> Self {
466 assert!(
467 size != BytePageSize::Unset,
468 "write buffer page size must be set"
469 );
470 self.write_size = size;
471 self
472 }
473
474 /// Sets the write buffer threshold.
475 ///
476 /// Application code can encode multiple items during one dispatcher turn.
477 /// Normally, the transport write task is scheduled to run after that work
478 /// yields, so all items produced during the turn may accumulate in the
479 /// write buffer and be sent as one large burst.
480 ///
481 /// When the buffered size reaches `size` while the transport write task is
482 /// paused, `Io` asks the transport handle to start writing immediately.
483 /// This eager write attempt occurs synchronously with buffer consolidation,
484 /// before the current application or dispatcher turn necessarily
485 /// completes. If bytes remain afterward, the normal write task is
486 /// scheduled to continue delivery. Data below the threshold is delivered
487 /// through that normal scheduled path.
488 ///
489 /// The threshold is a latency and write-burst tuning parameter. It does not
490 /// limit write-buffer growth, provide a flush guarantee, or control write
491 /// backpressure; use [`set_write_backpressure`](Self::set_write_backpressure) for
492 /// backpressure watermarks and [`Io::flush`](crate::Io::flush) when a
493 /// caller must wait for buffered data to be written.
494 ///
495 /// Set `size` to zero to disable eager writes when this configuration is
496 /// used to construct an [`Io`](crate::Io). The default is 8 KiB, derived
497 /// from the default 16 KiB page size when [`IoConfig::new`] is called.
498 /// Changing the page size later does not recalculate this threshold.
499 ///
500 /// Replacing an active connection's configuration with
501 /// [`Io::set_config`](crate::Io::set_config) enables or disables eager
502 /// writes according to the replacement threshold.
503 #[must_use]
504 pub fn set_write_buf_threshold(mut self, size: usize) -> Self {
505 self.write_buf_threshold = size;
506 self
507 }
508
509 /// Sets the write-buffer backpressure watermark.
510 ///
511 /// Write backpressure is enabled at `size` bytes of outstanding output and
512 /// must be greater than zero. Backpressure is released after the
513 /// outstanding size falls to half of this value. Outstanding output is the
514 /// buffered output plus any output a transport has taken ownership of but
515 /// not yet written to the peer.
516 ///
517 /// Output is held in [`BytePages`](ntex_bytes::BytePages), which are sized
518 /// by [`set_write_size`](Self::set_write_size).
519 ///
520 /// By default, the high watermark is approximately 16 KiB.
521 ///
522 /// # Panics
523 ///
524 /// Panics if `size` is zero.
525 #[must_use]
526 pub fn set_write_backpressure(mut self, size: usize) -> Self {
527 assert!(size > 0, "write backpressure must be greater than zero");
528 self.write_backpressure = size;
529 self
530 }
531}
532
533#[cfg(test)]
534mod tests {
535 use ntex_service::cfg::SharedCfg;
536
537 use super::*;
538
539 #[test]
540 fn buffer_configuration() {
541 let cfg = IoConfig::new()
542 .set_read_backpressure(4096)
543 .set_write_backpressure(2048);
544
545 assert_eq!(cfg.read_backpressure(), 4096);
546 assert_eq!(cfg.read_half(), 2048);
547 assert_eq!(cfg.write_backpressure(), 2048);
548 assert_eq!(cfg.write_half(), 1024);
549 }
550
551 #[test]
552 fn read_size_configuration() {
553 let default = BytePageSize::Size32.capacity();
554 let cfg = IoConfig::new();
555 assert_eq!(cfg.read_size_min(), BytePageSize::Size4);
556 assert_eq!(cfg.read_size_max(), BytePageSize::Size64);
557 assert_eq!(cfg.read_backpressure(), default);
558 assert_eq!(cfg.read_half(), default / 2);
559 assert_eq!(cfg.write_backpressure(), BytePageSize::Size16.capacity());
560
561 // the read size does not change the backpressure watermark
562 let cfg = cfg
563 .set_read_size(BytePageSize::Size8, BytePageSize::Size8)
564 .set_write_backpressure(2048);
565 assert_eq!(cfg.read_size_min(), BytePageSize::Size8);
566 assert_eq!(cfg.read_size_max(), BytePageSize::Size8);
567 assert_eq!(cfg.read_backpressure(), default);
568 assert_eq!(cfg.write_backpressure(), 2048);
569 assert_eq!(cfg.write_half(), 1024);
570
571 let cfg = cfg.set_read_size(BytePageSize::Size16, BytePageSize::Size256);
572 assert_eq!(cfg.read_size_min(), BytePageSize::Size16);
573 assert_eq!(cfg.read_size_max(), BytePageSize::Size256);
574 assert_eq!(cfg.read_backpressure(), default);
575 }
576
577 #[test]
578 fn read_backpressure_configuration() {
579 let cfg = IoConfig::new()
580 .set_read_backpressure(64 * 1024)
581 .set_read_size(BytePageSize::Size4, BytePageSize::Size4);
582 assert_eq!(cfg.read_size_min(), BytePageSize::Size4);
583 assert_eq!(cfg.read_backpressure(), 64 * 1024);
584 assert_eq!(cfg.read_half(), 32 * 1024);
585 }
586
587 #[test]
588 #[should_panic(expected = "read buffer page size must be set")]
589 fn unset_read_size() {
590 let _ = IoConfig::new().set_read_size(BytePageSize::Size4, BytePageSize::Unset);
591 }
592
593 #[test]
594 #[should_panic(expected = "read buffer min page size must not be larger than max")]
595 fn inverted_read_size() {
596 let _ = IoConfig::new().set_read_size(BytePageSize::Size16, BytePageSize::Size8);
597 }
598 #[test]
599 #[should_panic(expected = "write buffer page size must be set")]
600 fn unset_write_size() {
601 let _ = IoConfig::new().set_write_size(BytePageSize::Unset);
602 }
603
604 #[test]
605 #[should_panic(expected = "read backpressure must be greater than zero")]
606 fn zero_read_backpressure() {
607 let _ = IoConfig::new().set_read_backpressure(0);
608 }
609
610 #[test]
611 fn frame_read_rate_configuration() {
612 let cfg = IoConfig::new().set_frame_read_rate(Seconds(1), Seconds(3), 128);
613 let rate = cfg.frame_read_rate().unwrap();
614 assert_eq!(rate.timeout, Seconds(1));
615 assert_eq!(rate.max_timeout, Seconds(3));
616 assert_eq!(rate.rate, 128);
617
618 let cfg = cfg.set_frame_read_rate(Seconds::ZERO, Seconds(10), 1024);
619 assert!(cfg.frame_read_rate().is_none());
620 }
621
622 #[test]
623 fn config_accessors() {
624 let cfg = IoConfig::new();
625 assert_eq!(cfg.connect_timeout(), Millis::ZERO);
626 assert_eq!(cfg.keepalive_timeout(), Seconds(0));
627 assert_eq!(cfg.write_size(), BytePageSize::Size16);
628
629 let cfg = cfg
630 .set_connect_timeout(Millis(500))
631 .set_keepalive_timeout(Seconds(7))
632 .set_write_size(BytePageSize::Size4);
633 assert_eq!(cfg.connect_timeout(), Millis(500));
634 assert_eq!(cfg.keepalive_timeout(), Seconds(7));
635 assert_eq!(cfg.write_size(), BytePageSize::Size4);
636
637 let shared = SharedCfg::new("CFG-TAG").add(cfg).build();
638 let cfg = shared.get::<IoConfig>();
639 assert_eq!(cfg.tag(), "CFG-TAG");
640 assert_eq!(cfg.keepalive_timeout(), Seconds(7));
641 }
642
643 #[test]
644 fn write_timeout_configuration() {
645 let cfg = IoConfig::new();
646 assert!(cfg.write_timeout().is_zero());
647
648 let cfg = cfg.set_write_timeout(Seconds(3));
649 assert_eq!(cfg.write_timeout(), Seconds(3));
650
651 let cfg = cfg.set_write_timeout(Seconds::ZERO);
652 assert!(cfg.write_timeout().is_zero());
653 }
654
655 #[test]
656 #[should_panic(expected = "write backpressure must be greater than zero")]
657 fn zero_write_backpressure() {
658 let _ = IoConfig::new().set_write_backpressure(0);
659 }
660
661 #[test]
662 #[should_panic(expected = "shutdown timeout must be greater than zero")]
663 fn zero_shutdown_timeout_is_rejected() {
664 let _ = IoConfig::new().set_shutdown_timeout(Seconds::ZERO);
665 }
666}