1use std::time::Duration;
4
5use crate::addr::PrefixPolicy;
6use crate::driver::DriverKind;
7use crate::error::{ConfigReason, NetError};
8use crate::info::Lane;
9
10const RESERVED_LANES: usize = 8;
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
24pub struct MinRate {
25 pub bytes_per_sec: u32,
27 pub window: Duration,
29 pub grace: Duration,
31 pub check: Duration,
33}
34
35impl MinRate {
36 #[must_use]
38 pub const fn off() -> MinRate {
39 MinRate {
40 bytes_per_sec: 0,
41 window: Duration::from_secs(10),
42 grace: Duration::from_secs(5),
43 check: Duration::from_secs(1),
44 }
45 }
46
47 #[must_use]
49 pub const fn new(bytes_per_sec: u32, window: Duration) -> MinRate {
50 MinRate {
51 bytes_per_sec,
52 window,
53 grace: Duration::from_secs(5),
54 check: Duration::from_secs(1),
55 }
56 }
57
58 #[must_use]
60 pub const fn is_off(&self) -> bool {
61 self.bytes_per_sec == 0
62 }
63}
64
65#[derive(Clone, Copy, Debug, PartialEq)]
72pub struct Pressure {
73 pub high: f32,
75 pub low: f32,
77 pub factor: f32,
79}
80
81impl Default for Pressure {
82 fn default() -> Pressure {
83 Pressure {
84 high: 0.8,
85 low: 0.6,
86 factor: 0.25,
87 }
88 }
89}
90
91#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
100pub struct SizeClass(u32);
101
102impl SizeClass {
103 pub const SMALL: SizeClass = SizeClass(4 * 1024);
105 pub const MEDIUM: SizeClass = SizeClass(16 * 1024);
107 pub const LARGE: SizeClass = SizeClass(64 * 1024);
109
110 pub const fn new(bytes: u32) -> Result<SizeClass, NetError> {
116 if !bytes.is_power_of_two() {
117 return Err(NetError::InvalidConfig {
118 field: "size_class",
119 reason: ConfigReason::Inconsistent,
120 });
121 }
122 if bytes < 1024 {
123 return Err(NetError::InvalidConfig {
124 field: "size_class",
125 reason: ConfigReason::TooSmall { min: 1024 },
126 });
127 }
128 if bytes > 1024 * 1024 {
129 return Err(NetError::InvalidConfig {
130 field: "size_class",
131 reason: ConfigReason::TooLarge { max: 1024 * 1024 },
132 });
133 }
134 Ok(SizeClass(bytes))
135 }
136
137 #[must_use]
139 pub const fn bytes(self) -> u32 {
140 self.0
141 }
142}
143
144#[non_exhaustive]
164#[derive(Clone, Debug, Default, PartialEq)]
165pub struct Limits {
166 max_connections: Option<u32>,
167 max_per_listener: Option<u32>,
168 max_per_source: Option<(u32, PrefixPolicy)>,
169 backlog: Option<u32>,
170 accept_batch: Option<u16>,
171 handshake_timeout: Option<Duration>,
172 idle_timeout: Option<Duration>,
173 read_timeout: Option<Duration>,
174 write_timeout: Option<Duration>,
175 park_timeout: Option<Duration>,
176 min_rate: Option<MinRate>,
177 proxy_header_max: Option<u16>,
178 read_class: Option<(SizeClass, SizeClass)>,
179 output_budget: Option<u32>,
180 completion_queue: Option<u32>,
181 buffer_budget: Option<u64>,
182 drain_timeout: Option<Duration>,
183 graceful_linger: Option<(Duration, u32)>,
184 refusal_bytes_max: Option<u16>,
185 tick: Option<Duration>,
186 pressure: Option<Pressure>,
187 lane_reserve: [Option<u32>; RESERVED_LANES],
188}
189
190#[derive(Clone, Copy, Debug, PartialEq)]
192pub(crate) struct Effective {
193 pub max_connections: u32,
194 pub max_per_listener: u32,
195 pub max_per_source: u32,
196 pub source_policy: PrefixPolicy,
197 pub backlog: u32,
198 pub accept_batch: u16,
199 pub handshake_timeout: Duration,
200 pub idle_timeout: Duration,
201 pub read_timeout: Duration,
202 pub write_timeout: Duration,
203 pub park_timeout: Duration,
204 pub min_rate: MinRate,
205 pub proxy_header_max: u16,
206 pub read_class: SizeClass,
207 pub read_class_max: SizeClass,
208 pub output_budget: u32,
209 pub completion_queue: u32,
210 pub buffer_budget: u64,
211 pub drain_timeout: Duration,
212 pub graceful_linger: Duration,
213 pub graceful_linger_bytes: u32,
214 pub refusal_bytes_max: u16,
215 pub tick: Duration,
216}
217
218impl Effective {
219 pub(crate) fn defaults(driver: DriverKind) -> Effective {
221 let blocking = driver.resolve() == DriverKind::Blocking;
222 let max_connections = if blocking { 128 } else { 10_000 };
223 Effective {
224 max_connections,
225 max_per_listener: max_connections,
226 max_per_source: if blocking { 16 } else { 256 },
227 source_policy: PrefixPolicy::DEFAULT,
228 backlog: 1024,
229 accept_batch: if blocking { 16 } else { 64 },
230 handshake_timeout: Duration::from_secs(10),
231 idle_timeout: Duration::from_secs(if blocking { 5 } else { 60 }),
232 read_timeout: Duration::from_secs(30),
233 write_timeout: Duration::from_secs(30),
234 park_timeout: Duration::from_secs(30),
235 min_rate: MinRate::new(256, Duration::from_secs(10)),
236 proxy_header_max: 1024,
237 read_class: SizeClass::MEDIUM,
238 read_class_max: SizeClass::LARGE,
239 output_budget: 64 * 1024,
240 completion_queue: if blocking { 1 } else { 1024 },
241 buffer_budget: if blocking { 64 << 20 } else { 512 << 20 },
242 drain_timeout: Duration::from_secs(30),
243 graceful_linger: Duration::from_secs(2),
244 graceful_linger_bytes: 64 * 1024,
245 refusal_bytes_max: 512,
246 tick: Duration::from_millis(if blocking { 10 } else { 1 }),
247 }
248 }
249}
250
251impl Limits {
252 pub fn builder() -> LimitsBuilder {
260 LimitsBuilder::default()
261 }
262
263 #[must_use]
269 pub fn reactor_defaults() -> Limits {
270 Limits::from_effective(&Effective::defaults(DriverKind::Reactor))
271 }
272
273 #[must_use]
279 pub fn blocking_defaults() -> Limits {
280 Limits::from_effective(&Effective::defaults(DriverKind::Blocking))
281 }
282
283 pub fn to_builder(&self) -> LimitsBuilder {
295 LimitsBuilder {
296 limits: self.clone(),
297 }
298 }
299
300 #[must_use]
309 pub fn resolve(&self, driver: DriverKind) -> Limits {
310 let mut out = Limits::from_effective(&self.effective(driver));
311 out.pressure = self.pressure;
312 out.lane_reserve = self.lane_reserve;
313 out
314 }
315
316 pub fn validate(&self, workers: u16, descriptor_limit: Option<u64>) -> Result<(), NetError> {
329 LimitsBuilder {
331 limits: self.clone(),
332 }
333 .check()?;
334 let e = self.effective(DriverKind::Blocking);
335 let need_budget = u64::from(workers)
336 .saturating_mul(u64::from(e.read_class_max.bytes()))
337 .saturating_mul(2);
338 if e.buffer_budget < need_budget {
339 return Err(invalid(
340 "buffer_budget",
341 ConfigReason::BudgetBelowWorkers { need: need_budget },
342 ));
343 }
344 if let Some(have) = descriptor_limit {
345 let need = descriptors_needed(e.max_connections, workers);
346 if need > have {
347 return Err(invalid(
348 "max_connections",
349 ConfigReason::DescriptorLimit { have, need },
350 ));
351 }
352 }
353 Ok(())
354 }
355
356 pub(crate) fn effective(&self, driver: DriverKind) -> Effective {
358 let d = Effective::defaults(driver);
359 let max_connections = self.max_connections.unwrap_or(d.max_connections);
360 let (max_per_source, source_policy) = self
361 .max_per_source
362 .unwrap_or((d.max_per_source.min(max_connections), d.source_policy));
363 let (read_class, read_class_max) =
364 self.read_class.unwrap_or((d.read_class, d.read_class_max));
365 let (graceful_linger, graceful_linger_bytes) = self
366 .graceful_linger
367 .unwrap_or((d.graceful_linger, d.graceful_linger_bytes));
368 Effective {
369 max_connections,
370 max_per_listener: self.max_per_listener.unwrap_or(max_connections),
371 max_per_source,
372 source_policy,
373 backlog: self.backlog.unwrap_or(d.backlog),
374 accept_batch: self.accept_batch.unwrap_or(d.accept_batch),
375 handshake_timeout: self.handshake_timeout.unwrap_or(d.handshake_timeout),
376 idle_timeout: self.idle_timeout.unwrap_or(d.idle_timeout),
377 read_timeout: self.read_timeout.unwrap_or(d.read_timeout),
378 write_timeout: self.write_timeout.unwrap_or(d.write_timeout),
379 park_timeout: self.park_timeout.unwrap_or(d.park_timeout),
380 min_rate: self.min_rate.unwrap_or(d.min_rate),
381 proxy_header_max: self.proxy_header_max.unwrap_or(d.proxy_header_max),
382 read_class,
383 read_class_max,
384 output_budget: self.output_budget.unwrap_or(d.output_budget),
385 completion_queue: self.completion_queue.unwrap_or(d.completion_queue),
386 buffer_budget: self.buffer_budget.unwrap_or(d.buffer_budget),
387 drain_timeout: self.drain_timeout.unwrap_or(d.drain_timeout),
388 graceful_linger,
389 graceful_linger_bytes,
390 refusal_bytes_max: self.refusal_bytes_max.unwrap_or(d.refusal_bytes_max),
391 tick: self.tick.unwrap_or(d.tick),
392 }
393 }
394
395 pub(crate) fn overlay(&self, other: &Limits) -> Limits {
397 macro_rules! pick {
398 ($($f:ident),*) => { Limits { $($f: other.$f.or(self.$f),)* lane_reserve: {
399 let mut r = self.lane_reserve;
400 for (i, v) in other.lane_reserve.iter().enumerate() { if v.is_some() { r[i] = *v; } }
401 r
402 } } };
403 }
404 pick!(
405 max_connections,
406 max_per_listener,
407 max_per_source,
408 backlog,
409 accept_batch,
410 handshake_timeout,
411 idle_timeout,
412 read_timeout,
413 write_timeout,
414 park_timeout,
415 min_rate,
416 proxy_header_max,
417 read_class,
418 output_budget,
419 completion_queue,
420 buffer_budget,
421 drain_timeout,
422 graceful_linger,
423 refusal_bytes_max,
424 tick,
425 pressure
426 )
427 }
428
429 fn from_effective(e: &Effective) -> Limits {
430 Limits {
431 max_connections: Some(e.max_connections),
432 max_per_listener: Some(e.max_per_listener),
433 max_per_source: Some((e.max_per_source, e.source_policy)),
434 backlog: Some(e.backlog),
435 accept_batch: Some(e.accept_batch),
436 handshake_timeout: Some(e.handshake_timeout),
437 idle_timeout: Some(e.idle_timeout),
438 read_timeout: Some(e.read_timeout),
439 write_timeout: Some(e.write_timeout),
440 park_timeout: Some(e.park_timeout),
441 min_rate: Some(e.min_rate),
442 proxy_header_max: Some(e.proxy_header_max),
443 read_class: Some((e.read_class, e.read_class_max)),
444 output_budget: Some(e.output_budget),
445 completion_queue: Some(e.completion_queue),
446 buffer_budget: Some(e.buffer_budget),
447 drain_timeout: Some(e.drain_timeout),
448 graceful_linger: Some((e.graceful_linger, e.graceful_linger_bytes)),
449 refusal_bytes_max: Some(e.refusal_bytes_max),
450 tick: Some(e.tick),
451 pressure: None,
452 lane_reserve: [None; RESERVED_LANES],
453 }
454 }
455
456 #[must_use]
458 pub fn max_connections(&self) -> Option<u32> {
459 self.max_connections
460 }
461 #[must_use]
463 pub fn max_per_listener(&self) -> Option<u32> {
464 self.max_per_listener
465 }
466 #[must_use]
468 pub fn max_per_source(&self) -> Option<(u32, PrefixPolicy)> {
469 self.max_per_source
470 }
471 #[must_use]
473 pub fn backlog(&self) -> Option<u32> {
474 self.backlog
475 }
476 #[must_use]
478 pub fn accept_batch(&self) -> Option<u16> {
479 self.accept_batch
480 }
481 #[must_use]
483 pub fn handshake_timeout(&self) -> Option<Duration> {
484 self.handshake_timeout
485 }
486 #[must_use]
488 pub fn idle_timeout(&self) -> Option<Duration> {
489 self.idle_timeout
490 }
491 #[must_use]
493 pub fn read_timeout(&self) -> Option<Duration> {
494 self.read_timeout
495 }
496 #[must_use]
498 pub fn write_timeout(&self) -> Option<Duration> {
499 self.write_timeout
500 }
501 #[must_use]
503 pub fn park_timeout(&self) -> Option<Duration> {
504 self.park_timeout
505 }
506 #[must_use]
508 pub fn min_rate(&self) -> Option<MinRate> {
509 self.min_rate
510 }
511 #[must_use]
513 pub fn proxy_header_max(&self) -> Option<u16> {
514 self.proxy_header_max
515 }
516 #[must_use]
518 pub fn read_class(&self) -> Option<(SizeClass, SizeClass)> {
519 self.read_class
520 }
521 #[must_use]
523 pub fn output_budget(&self) -> Option<u32> {
524 self.output_budget
525 }
526 #[must_use]
528 pub fn completion_queue(&self) -> Option<u32> {
529 self.completion_queue
530 }
531 #[must_use]
533 pub fn buffer_budget(&self) -> Option<u64> {
534 self.buffer_budget
535 }
536 #[must_use]
538 pub fn drain_timeout(&self) -> Option<Duration> {
539 self.drain_timeout
540 }
541 #[must_use]
543 pub fn graceful_linger(&self) -> Option<(Duration, u32)> {
544 self.graceful_linger
545 }
546 #[must_use]
548 pub fn refusal_bytes_max(&self) -> Option<u16> {
549 self.refusal_bytes_max
550 }
551 #[must_use]
553 pub fn tick(&self) -> Option<Duration> {
554 self.tick
555 }
556 #[must_use]
558 pub fn pressure(&self) -> Option<Pressure> {
559 self.pressure
560 }
561 #[must_use]
563 pub fn lane_reserve(&self, lane: Lane) -> Option<u32> {
564 self.lane_reserve
565 .get(usize::from(lane.0))
566 .copied()
567 .flatten()
568 }
569}
570
571pub(crate) fn descriptors_needed(max_connections: u32, workers: u16) -> u64 {
574 u64::from(max_connections) + 2 * u64::from(workers) + 32
575}
576
577fn invalid(field: &'static str, reason: ConfigReason) -> NetError {
578 NetError::InvalidConfig { field, reason }
579}
580
581#[must_use]
590#[derive(Clone, Debug, Default)]
591pub struct LimitsBuilder {
592 limits: Limits,
593}
594
595macro_rules! setter {
596 ($(#[$m:meta])* $name:ident, $t:ty) => {
597 $(#[$m])*
598 pub fn $name(mut self, value: $t) -> Self {
599 self.limits.$name = Some(value);
600 self
601 }
602 };
603}
604
605impl LimitsBuilder {
606 setter!(max_connections, u32);
608 setter!(max_per_listener, u32);
610 setter!(backlog, u32);
612 setter!(accept_batch, u16);
614 setter!(handshake_timeout, Duration);
616 setter!(idle_timeout, Duration);
618 setter!(read_timeout, Duration);
620 setter!(write_timeout, Duration);
622 setter!(park_timeout, Duration);
624 setter!(min_rate, MinRate);
626 setter!(output_budget, u32);
628 setter!(buffer_budget, u64);
630 setter!(drain_timeout, Duration);
632 setter!(refusal_bytes_max, u16);
634 setter!(tick, Duration);
636 setter!(pressure, Pressure);
638
639 pub fn max_per_source(mut self, n: u32, policy: PrefixPolicy) -> Self {
641 self.limits.max_per_source = Some((n, policy));
642 self
643 }
644
645 pub fn proxy_header_max(mut self, v2_body_bytes: u16) -> Self {
647 self.limits.proxy_header_max = Some(v2_body_bytes);
648 self
649 }
650
651 pub fn read_class(mut self, initial: SizeClass, max: SizeClass) -> Self {
653 self.limits.read_class = Some((initial, max));
654 self
655 }
656
657 pub fn completion_queue(mut self, n: u32) -> Self {
659 self.limits.completion_queue = Some(n);
660 self
661 }
662
663 pub fn graceful_linger(mut self, time: Duration, bytes: u32) -> Self {
665 self.limits.graceful_linger = Some((time, bytes));
666 self
667 }
668
669 pub fn lane_reserve(mut self, lane: Lane, slots: u32) -> Self {
671 match self.limits.lane_reserve.get_mut(usize::from(lane.0)) {
672 Some(slot) => *slot = Some(slots),
673 None => self.limits.lane_reserve[RESERVED_LANES - 1] = Some(u32::MAX),
675 }
676 self
677 }
678
679 pub fn build(self) -> Result<Limits, NetError> {
686 self.check()?;
687 let mut limits = self.limits;
688 if let Some(n) = limits.completion_queue {
689 limits.completion_queue = Some(n.next_power_of_two());
690 }
691 Ok(limits)
692 }
693
694 fn check(&self) -> Result<(), NetError> {
695 let l = &self.limits;
696 let zeros: [(&'static str, bool); 6] = [
698 ("max_connections", l.max_connections == Some(0)),
699 ("backlog", l.backlog == Some(0)),
700 ("accept_batch", l.accept_batch == Some(0)),
701 ("max_per_source", matches!(l.max_per_source, Some((0, _)))),
702 ("completion_queue", l.completion_queue == Some(0)),
703 ("output_budget", l.output_budget == Some(0)),
704 ];
705 for (field, zero) in zeros {
706 if zero {
707 return Err(invalid(field, ConfigReason::Zero));
708 }
709 }
710 if let Some(max) = l.max_connections {
711 if l.max_per_listener.is_some_and(|n| n > max) {
712 return Err(invalid("max_per_listener", ConfigReason::AboveGlobal));
713 }
714 if l.max_per_source.is_some_and(|(n, _)| n > max) {
715 return Err(invalid("max_per_source", ConfigReason::AboveGlobal));
716 }
717 }
718 if let Some(tick) = l.tick {
719 if tick < Duration::from_millis(1) {
720 return Err(invalid("tick", ConfigReason::TooSmall { min: 1 }));
721 }
722 if tick > Duration::from_millis(100) {
723 return Err(invalid("tick", ConfigReason::TooLarge { max: 100 }));
724 }
725 }
726 let tick = l.tick.unwrap_or(Duration::from_millis(1));
727 let timeouts: [(&'static str, Option<Duration>); 6] = [
728 ("handshake_timeout", l.handshake_timeout),
729 ("idle_timeout", l.idle_timeout),
730 ("read_timeout", l.read_timeout),
731 ("write_timeout", l.write_timeout),
732 ("park_timeout", l.park_timeout),
733 ("drain_timeout", l.drain_timeout),
734 ];
735 for (field, value) in timeouts {
736 if value.is_some_and(|d| d < tick) {
737 return Err(invalid(field, ConfigReason::BelowTick));
738 }
739 }
740 if let Some(rate) = l.min_rate {
741 if !rate.is_off() {
742 if rate.check.is_zero() || rate.grace.is_zero() {
743 return Err(invalid("min_rate", ConfigReason::Zero));
744 }
745 if rate.window < rate.check {
746 return Err(invalid("min_rate", ConfigReason::WindowBelowCheck));
747 }
748 }
749 }
750 if let Some((initial, max)) = l.read_class {
751 if max < initial {
752 return Err(invalid("read_class", ConfigReason::Inconsistent));
753 }
754 }
755 if l.refusal_bytes_max.is_some_and(|n| n > 512) {
756 return Err(invalid(
757 "refusal_bytes_max",
758 ConfigReason::TooLarge { max: 512 },
759 ));
760 }
761 if l.proxy_header_max.is_some_and(|n| n > 65_519) {
762 return Err(invalid(
763 "proxy_header_max",
764 ConfigReason::TooLarge { max: 65_519 },
765 ));
766 }
767 if l.completion_queue.is_some_and(|n| n > 1 << 20) {
768 return Err(invalid(
769 "completion_queue",
770 ConfigReason::TooLarge { max: 1 << 20 },
771 ));
772 }
773 if let Some(p) = l.pressure {
774 let factor_ok = p.factor > 0.0 && p.factor <= 1.0;
775 let ordered = p.high.partial_cmp(&p.low) == Some(std::cmp::Ordering::Greater);
776 if !ordered
777 || !factor_ok
778 || !(0.0..=1.0).contains(&p.high)
779 || !(0.0..=1.0).contains(&p.low)
780 {
781 return Err(invalid("pressure", ConfigReason::Inconsistent));
782 }
783 }
784 if l.lane_reserve[RESERVED_LANES - 1] == Some(u32::MAX) {
785 return Err(invalid("lane_reserve", ConfigReason::TooLarge { max: 7 }));
786 }
787 let reserved: u64 = l.lane_reserve.iter().flatten().map(|n| u64::from(*n)).sum();
788 if let Some(max) = l.max_connections {
789 if reserved > u64::from(max) {
790 return Err(invalid("lane_reserve", ConfigReason::AboveGlobal));
791 }
792 }
793 Ok(())
794 }
795}
796
797#[cfg(test)]
798mod tests {
799 use super::*;
800
801 #[test]
802 fn test_unset_values_take_driver_defaults() {
803 let l = Limits::builder().build().unwrap();
804 assert_eq!(
805 l.effective(DriverKind::Blocking).idle_timeout,
806 Duration::from_secs(5)
807 );
808 assert_eq!(
809 l.effective(DriverKind::Reactor).idle_timeout,
810 Duration::from_secs(60)
811 );
812 }
813
814 #[test]
815 fn test_dependent_defaults_follow_max_connections() {
816 let l = Limits::builder().max_connections(10).build().unwrap();
817 let e = l.effective(DriverKind::Reactor);
818 assert_eq!(e.max_per_listener, 10);
819 assert_eq!(e.max_per_source, 10);
820 }
821
822 #[test]
823 fn test_zero_values_rejected() {
824 for b in [
825 Limits::builder().max_connections(0),
826 Limits::builder().backlog(0),
827 Limits::builder().accept_batch(0),
828 Limits::builder().max_per_source(0, PrefixPolicy::DEFAULT),
829 Limits::builder().completion_queue(0),
830 Limits::builder().output_budget(0),
831 ] {
832 assert!(matches!(
833 b.build(),
834 Err(NetError::InvalidConfig {
835 reason: ConfigReason::Zero,
836 ..
837 })
838 ));
839 }
840 }
841
842 #[test]
843 fn test_timeout_below_tick_rejected() {
844 let err = Limits::builder()
845 .tick(Duration::from_millis(100))
846 .read_timeout(Duration::from_millis(50))
847 .build()
848 .unwrap_err();
849 assert!(matches!(
850 err,
851 NetError::InvalidConfig {
852 field: "read_timeout",
853 reason: ConfigReason::BelowTick
854 }
855 ));
856 }
857
858 #[test]
859 fn test_min_rate_window_below_check_rejected() {
860 let rate = MinRate {
861 bytes_per_sec: 10,
862 window: Duration::from_millis(500),
863 grace: Duration::from_secs(1),
864 check: Duration::from_secs(1),
865 };
866 assert!(matches!(
867 Limits::builder().min_rate(rate).build(),
868 Err(NetError::InvalidConfig {
869 reason: ConfigReason::WindowBelowCheck,
870 ..
871 })
872 ));
873 assert!(Limits::builder().min_rate(MinRate::off()).build().is_ok());
874 }
875
876 #[test]
877 fn test_bounds_too_large() {
878 assert!(Limits::builder().refusal_bytes_max(513).build().is_err());
879 assert!(Limits::builder().refusal_bytes_max(512).build().is_ok());
880 assert!(Limits::builder().proxy_header_max(65_520).build().is_err());
881 assert!(
882 Limits::builder()
883 .completion_queue((1 << 20) + 1)
884 .build()
885 .is_err()
886 );
887 }
888
889 #[test]
890 fn test_completion_queue_rounded_to_power_of_two() {
891 let l = Limits::builder().completion_queue(1000).build().unwrap();
892 assert_eq!(l.completion_queue(), Some(1024));
893 }
894
895 #[test]
896 fn test_pressure_and_lane_reserve_validation() {
897 let bad = Pressure {
898 high: 0.5,
899 low: 0.6,
900 factor: 0.5,
901 };
902 assert!(Limits::builder().pressure(bad).build().is_err());
903 assert!(Limits::builder().lane_reserve(Lane(8), 1).build().is_err());
904 assert!(
905 Limits::builder()
906 .max_connections(10)
907 .lane_reserve(Lane(0), 6)
908 .lane_reserve(Lane(1), 5)
909 .build()
910 .is_err()
911 );
912 }
913
914 #[test]
915 fn test_validate_descriptor_limit_and_budget() {
916 let l = Limits::builder().max_connections(1000).build().unwrap();
917 assert!(matches!(
918 l.validate(4, Some(500)),
919 Err(NetError::InvalidConfig {
920 reason: ConfigReason::DescriptorLimit { have: 500, .. },
921 ..
922 })
923 ));
924 assert!(l.validate(4, Some(100_000)).is_ok());
925 let small = Limits::builder().buffer_budget(1024).build().unwrap();
926 assert!(matches!(
927 small.validate(4, None),
928 Err(NetError::InvalidConfig {
929 reason: ConfigReason::BudgetBelowWorkers { .. },
930 ..
931 })
932 ));
933 }
934
935 #[test]
936 fn test_overlay_takes_listener_values() {
937 let base = Limits::builder()
938 .max_connections(100)
939 .idle_timeout(Duration::from_secs(9))
940 .build()
941 .unwrap();
942 let over = Limits::builder()
943 .idle_timeout(Duration::from_secs(1))
944 .build()
945 .unwrap();
946 let merged = base.overlay(&over);
947 assert_eq!(merged.max_connections(), Some(100));
948 assert_eq!(merged.idle_timeout(), Some(Duration::from_secs(1)));
949 }
950
951 #[test]
952 fn test_resolve_sets_every_value() {
953 let r = Limits::default().resolve(DriverKind::Blocking);
954 assert!(
955 r.max_connections().is_some() && r.tick().is_some() && r.graceful_linger().is_some()
956 );
957 }
958}