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)]
75pub struct Pressure {
76 pub high: f32,
78 pub low: f32,
80 pub factor: f32,
82}
83
84impl Default for Pressure {
85 fn default() -> Pressure {
86 Pressure {
87 high: 0.8,
88 low: 0.6,
89 factor: 0.25,
90 }
91 }
92}
93
94#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
103pub struct SizeClass(u32);
104
105impl SizeClass {
106 pub const SMALL: SizeClass = SizeClass(4 * 1024);
108 pub const MEDIUM: SizeClass = SizeClass(16 * 1024);
110 pub const LARGE: SizeClass = SizeClass(64 * 1024);
112
113 pub const fn new(bytes: u32) -> Result<SizeClass, NetError> {
119 if !bytes.is_power_of_two() {
120 return Err(NetError::InvalidConfig {
121 field: "size_class",
122 reason: ConfigReason::Inconsistent,
123 });
124 }
125 if bytes < 1024 {
126 return Err(NetError::InvalidConfig {
127 field: "size_class",
128 reason: ConfigReason::TooSmall { min: 1024 },
129 });
130 }
131 if bytes > 1024 * 1024 {
132 return Err(NetError::InvalidConfig {
133 field: "size_class",
134 reason: ConfigReason::TooLarge { max: 1024 * 1024 },
135 });
136 }
137 Ok(SizeClass(bytes))
138 }
139
140 #[must_use]
142 pub const fn bytes(self) -> u32 {
143 self.0
144 }
145}
146
147#[non_exhaustive]
167#[derive(Clone, Debug, Default, PartialEq)]
168pub struct Limits {
169 max_connections: Option<u32>,
170 max_per_listener: Option<u32>,
171 max_per_source: Option<(u32, PrefixPolicy)>,
172 backlog: Option<u32>,
173 accept_batch: Option<u16>,
174 handshake_timeout: Option<Duration>,
175 idle_timeout: Option<Duration>,
176 read_timeout: Option<Duration>,
177 write_timeout: Option<Duration>,
178 park_timeout: Option<Duration>,
179 min_rate: Option<MinRate>,
180 proxy_header_max: Option<u16>,
181 read_class: Option<(SizeClass, SizeClass)>,
182 output_budget: Option<u32>,
183 completion_queue: Option<u32>,
184 buffer_budget: Option<u64>,
185 drain_timeout: Option<Duration>,
186 graceful_linger: Option<(Duration, u32)>,
187 refusal_bytes_max: Option<u16>,
188 tick: Option<Duration>,
189 pressure: Option<Pressure>,
190 lane_reserve: [Option<u32>; RESERVED_LANES],
191}
192
193#[derive(Clone, Copy, Debug, PartialEq)]
195pub(crate) struct Effective {
196 pub max_connections: u32,
197 pub max_per_listener: u32,
198 pub max_per_source: u32,
199 pub source_policy: PrefixPolicy,
200 pub backlog: u32,
201 pub accept_batch: u16,
202 pub handshake_timeout: Duration,
203 pub idle_timeout: Duration,
204 pub read_timeout: Duration,
205 pub write_timeout: Duration,
206 pub park_timeout: Duration,
207 pub min_rate: MinRate,
208 pub proxy_header_max: u16,
209 pub read_class: SizeClass,
210 pub read_class_max: SizeClass,
211 pub output_budget: u32,
212 pub completion_queue: u32,
213 pub buffer_budget: u64,
214 pub drain_timeout: Duration,
215 pub graceful_linger: Duration,
216 pub graceful_linger_bytes: u32,
217 pub refusal_bytes_max: u16,
218 pub tick: Duration,
219 pub pressure: Option<Pressure>,
220 pub lane_reserve: [u32; RESERVED_LANES],
222}
223
224impl Effective {
225 pub(crate) fn reserved_above(&self, lane: Lane) -> u32 {
227 self.lane_reserve
228 .iter()
229 .take(usize::from(lane.0))
230 .fold(0u32, |sum, r| sum.saturating_add(*r))
231 }
232
233 pub(crate) fn defaults(driver: DriverKind) -> Effective {
235 let blocking = driver.resolve() == DriverKind::Blocking;
236 let max_connections = if blocking { 128 } else { 10_000 };
237 Effective {
238 max_connections,
239 max_per_listener: max_connections,
240 max_per_source: if blocking { 16 } else { 256 },
241 source_policy: PrefixPolicy::DEFAULT,
242 backlog: 1024,
243 accept_batch: if blocking { 16 } else { 64 },
244 handshake_timeout: Duration::from_secs(10),
245 idle_timeout: Duration::from_secs(if blocking { 5 } else { 60 }),
246 read_timeout: Duration::from_secs(30),
247 write_timeout: Duration::from_secs(30),
248 park_timeout: Duration::from_secs(30),
249 min_rate: MinRate::new(256, Duration::from_secs(10)),
250 proxy_header_max: 1024,
251 read_class: SizeClass::MEDIUM,
252 read_class_max: SizeClass::LARGE,
253 output_budget: 64 * 1024,
254 completion_queue: if blocking { 1 } else { 1024 },
255 buffer_budget: if blocking { 64 << 20 } else { 512 << 20 },
256 drain_timeout: Duration::from_secs(30),
257 graceful_linger: Duration::from_secs(2),
258 graceful_linger_bytes: 64 * 1024,
259 refusal_bytes_max: 512,
260 tick: Duration::from_millis(if blocking { 10 } else { 1 }),
261 pressure: None,
262 lane_reserve: [0; RESERVED_LANES],
263 }
264 }
265}
266
267impl Limits {
268 pub fn builder() -> LimitsBuilder {
276 LimitsBuilder::default()
277 }
278
279 #[must_use]
285 pub fn reactor_defaults() -> Limits {
286 Limits::from_effective(&Effective::defaults(DriverKind::Reactor))
287 }
288
289 #[must_use]
295 pub fn blocking_defaults() -> Limits {
296 Limits::from_effective(&Effective::defaults(DriverKind::Blocking))
297 }
298
299 pub fn to_builder(&self) -> LimitsBuilder {
311 LimitsBuilder {
312 limits: self.clone(),
313 }
314 }
315
316 #[must_use]
325 pub fn resolve(&self, driver: DriverKind) -> Limits {
326 let mut out = Limits::from_effective(&self.effective(driver));
327 out.pressure = self.pressure;
328 out.lane_reserve = self.lane_reserve;
329 out
330 }
331
332 pub fn validate(&self, workers: u16, descriptor_limit: Option<u64>) -> Result<(), NetError> {
345 LimitsBuilder {
347 limits: self.clone(),
348 }
349 .check()?;
350 let e = self.effective(DriverKind::Blocking);
351 let need_budget = u64::from(workers)
352 .saturating_mul(u64::from(e.read_class_max.bytes()))
353 .saturating_mul(2);
354 if e.buffer_budget < need_budget {
355 return Err(invalid(
356 "buffer_budget",
357 ConfigReason::BudgetBelowWorkers { need: need_budget },
358 ));
359 }
360 if let Some(have) = descriptor_limit {
361 let need = descriptors_needed(e.max_connections, workers);
362 if need > have {
363 return Err(invalid(
364 "max_connections",
365 ConfigReason::DescriptorLimit { have, need },
366 ));
367 }
368 }
369 Ok(())
370 }
371
372 pub(crate) fn effective(&self, driver: DriverKind) -> Effective {
374 let d = Effective::defaults(driver);
375 let max_connections = self.max_connections.unwrap_or(d.max_connections);
376 let (max_per_source, source_policy) = self
377 .max_per_source
378 .unwrap_or((d.max_per_source.min(max_connections), d.source_policy));
379 let (read_class, read_class_max) =
380 self.read_class.unwrap_or((d.read_class, d.read_class_max));
381 let (graceful_linger, graceful_linger_bytes) = self
382 .graceful_linger
383 .unwrap_or((d.graceful_linger, d.graceful_linger_bytes));
384 Effective {
385 max_connections,
386 max_per_listener: self.max_per_listener.unwrap_or(max_connections),
387 max_per_source,
388 source_policy,
389 backlog: self.backlog.unwrap_or(d.backlog),
390 accept_batch: self.accept_batch.unwrap_or(d.accept_batch),
391 handshake_timeout: self.handshake_timeout.unwrap_or(d.handshake_timeout),
392 idle_timeout: self.idle_timeout.unwrap_or(d.idle_timeout),
393 read_timeout: self.read_timeout.unwrap_or(d.read_timeout),
394 write_timeout: self.write_timeout.unwrap_or(d.write_timeout),
395 park_timeout: self.park_timeout.unwrap_or(d.park_timeout),
396 min_rate: self.min_rate.unwrap_or(d.min_rate),
397 proxy_header_max: self.proxy_header_max.unwrap_or(d.proxy_header_max),
398 read_class,
399 read_class_max,
400 output_budget: self.output_budget.unwrap_or(d.output_budget),
401 completion_queue: self.completion_queue.unwrap_or(d.completion_queue),
402 buffer_budget: self.buffer_budget.unwrap_or(d.buffer_budget),
403 drain_timeout: self.drain_timeout.unwrap_or(d.drain_timeout),
404 graceful_linger,
405 graceful_linger_bytes,
406 refusal_bytes_max: self.refusal_bytes_max.unwrap_or(d.refusal_bytes_max),
407 tick: self.tick.unwrap_or(d.tick),
408 pressure: self.pressure,
409 lane_reserve: self.lane_reserve.map(|v| v.unwrap_or(0)),
410 }
411 }
412
413 pub(crate) fn overlay(&self, other: &Limits) -> Limits {
415 macro_rules! pick {
416 ($($f:ident),*) => { Limits { $($f: other.$f.or(self.$f),)* lane_reserve: {
417 let mut r = self.lane_reserve;
418 for (i, v) in other.lane_reserve.iter().enumerate() { if v.is_some() { r[i] = *v; } }
419 r
420 } } };
421 }
422 pick!(
423 max_connections,
424 max_per_listener,
425 max_per_source,
426 backlog,
427 accept_batch,
428 handshake_timeout,
429 idle_timeout,
430 read_timeout,
431 write_timeout,
432 park_timeout,
433 min_rate,
434 proxy_header_max,
435 read_class,
436 output_budget,
437 completion_queue,
438 buffer_budget,
439 drain_timeout,
440 graceful_linger,
441 refusal_bytes_max,
442 tick,
443 pressure
444 )
445 }
446
447 fn from_effective(e: &Effective) -> Limits {
448 Limits {
449 max_connections: Some(e.max_connections),
450 max_per_listener: Some(e.max_per_listener),
451 max_per_source: Some((e.max_per_source, e.source_policy)),
452 backlog: Some(e.backlog),
453 accept_batch: Some(e.accept_batch),
454 handshake_timeout: Some(e.handshake_timeout),
455 idle_timeout: Some(e.idle_timeout),
456 read_timeout: Some(e.read_timeout),
457 write_timeout: Some(e.write_timeout),
458 park_timeout: Some(e.park_timeout),
459 min_rate: Some(e.min_rate),
460 proxy_header_max: Some(e.proxy_header_max),
461 read_class: Some((e.read_class, e.read_class_max)),
462 output_budget: Some(e.output_budget),
463 completion_queue: Some(e.completion_queue),
464 buffer_budget: Some(e.buffer_budget),
465 drain_timeout: Some(e.drain_timeout),
466 graceful_linger: Some((e.graceful_linger, e.graceful_linger_bytes)),
467 refusal_bytes_max: Some(e.refusal_bytes_max),
468 tick: Some(e.tick),
469 pressure: None,
470 lane_reserve: [None; RESERVED_LANES],
471 }
472 }
473
474 #[must_use]
476 pub fn max_connections(&self) -> Option<u32> {
477 self.max_connections
478 }
479 #[must_use]
481 pub fn max_per_listener(&self) -> Option<u32> {
482 self.max_per_listener
483 }
484 #[must_use]
489 pub fn max_per_source(&self) -> Option<(u32, PrefixPolicy)> {
490 self.max_per_source
491 }
492 #[must_use]
494 pub fn backlog(&self) -> Option<u32> {
495 self.backlog
496 }
497 #[must_use]
499 pub fn accept_batch(&self) -> Option<u16> {
500 self.accept_batch
501 }
502 #[must_use]
504 pub fn handshake_timeout(&self) -> Option<Duration> {
505 self.handshake_timeout
506 }
507 #[must_use]
509 pub fn idle_timeout(&self) -> Option<Duration> {
510 self.idle_timeout
511 }
512 #[must_use]
514 pub fn read_timeout(&self) -> Option<Duration> {
515 self.read_timeout
516 }
517 #[must_use]
519 pub fn write_timeout(&self) -> Option<Duration> {
520 self.write_timeout
521 }
522 #[must_use]
524 pub fn park_timeout(&self) -> Option<Duration> {
525 self.park_timeout
526 }
527 #[must_use]
529 pub fn min_rate(&self) -> Option<MinRate> {
530 self.min_rate
531 }
532 #[must_use]
534 pub fn proxy_header_max(&self) -> Option<u16> {
535 self.proxy_header_max
536 }
537 #[must_use]
539 pub fn read_class(&self) -> Option<(SizeClass, SizeClass)> {
540 self.read_class
541 }
542 #[must_use]
544 pub fn output_budget(&self) -> Option<u32> {
545 self.output_budget
546 }
547 #[must_use]
549 pub fn completion_queue(&self) -> Option<u32> {
550 self.completion_queue
551 }
552 #[must_use]
554 pub fn buffer_budget(&self) -> Option<u64> {
555 self.buffer_budget
556 }
557 #[must_use]
559 pub fn drain_timeout(&self) -> Option<Duration> {
560 self.drain_timeout
561 }
562 #[must_use]
564 pub fn graceful_linger(&self) -> Option<(Duration, u32)> {
565 self.graceful_linger
566 }
567 #[must_use]
569 pub fn refusal_bytes_max(&self) -> Option<u16> {
570 self.refusal_bytes_max
571 }
572 #[must_use]
574 pub fn tick(&self) -> Option<Duration> {
575 self.tick
576 }
577 #[must_use]
579 pub fn pressure(&self) -> Option<Pressure> {
580 self.pressure
581 }
582 #[must_use]
584 pub fn lane_reserve(&self, lane: Lane) -> Option<u32> {
585 self.lane_reserve
586 .get(usize::from(lane.0))
587 .copied()
588 .flatten()
589 }
590}
591
592pub(crate) fn descriptors_needed(max_connections: u32, workers: u16) -> u64 {
595 u64::from(max_connections) + 2 * u64::from(workers) + 32
596}
597
598fn invalid(field: &'static str, reason: ConfigReason) -> NetError {
599 NetError::InvalidConfig { field, reason }
600}
601
602#[must_use]
611#[derive(Clone, Debug, Default)]
612pub struct LimitsBuilder {
613 limits: Limits,
614}
615
616macro_rules! setter {
617 ($(#[$m:meta])* $name:ident, $t:ty) => {
618 $(#[$m])*
619 pub fn $name(mut self, value: $t) -> Self {
620 self.limits.$name = Some(value);
621 self
622 }
623 };
624}
625
626impl LimitsBuilder {
627 setter!(max_connections, u32);
629 setter!(max_per_listener, u32);
631 setter!(backlog, u32);
633 setter!(accept_batch, u16);
635 setter!(handshake_timeout, Duration);
637 setter!(idle_timeout, Duration);
639 setter!(read_timeout, Duration);
641 setter!(write_timeout, Duration);
643 setter!(park_timeout, Duration);
645 setter!(min_rate, MinRate);
647 setter!(output_budget, u32);
649 setter!(buffer_budget, u64);
651 setter!(drain_timeout, Duration);
653 setter!(refusal_bytes_max, u16);
655 setter!(tick, Duration);
657 setter!(pressure, Pressure);
659
660 pub fn max_per_source(mut self, n: u32, policy: PrefixPolicy) -> Self {
662 self.limits.max_per_source = Some((n, policy));
663 self
664 }
665
666 pub fn proxy_header_max(mut self, v2_body_bytes: u16) -> Self {
668 self.limits.proxy_header_max = Some(v2_body_bytes);
669 self
670 }
671
672 pub fn read_class(mut self, initial: SizeClass, max: SizeClass) -> Self {
674 self.limits.read_class = Some((initial, max));
675 self
676 }
677
678 pub fn completion_queue(mut self, n: u32) -> Self {
680 self.limits.completion_queue = Some(n);
681 self
682 }
683
684 pub fn graceful_linger(mut self, time: Duration, bytes: u32) -> Self {
686 self.limits.graceful_linger = Some((time, bytes));
687 self
688 }
689
690 pub fn lane_reserve(mut self, lane: Lane, slots: u32) -> Self {
692 match self.limits.lane_reserve.get_mut(usize::from(lane.0)) {
693 Some(slot) => *slot = Some(slots),
694 None => self.limits.lane_reserve[RESERVED_LANES - 1] = Some(u32::MAX),
696 }
697 self
698 }
699
700 pub fn build(self) -> Result<Limits, NetError> {
707 self.check()?;
708 let mut limits = self.limits;
709 if let Some(n) = limits.completion_queue {
710 limits.completion_queue = Some(n.next_power_of_two());
711 }
712 Ok(limits)
713 }
714
715 fn check(&self) -> Result<(), NetError> {
716 let l = &self.limits;
717 let zeros: [(&'static str, bool); 6] = [
719 ("max_connections", l.max_connections == Some(0)),
720 ("backlog", l.backlog == Some(0)),
721 ("accept_batch", l.accept_batch == Some(0)),
722 ("max_per_source", matches!(l.max_per_source, Some((0, _)))),
723 ("completion_queue", l.completion_queue == Some(0)),
724 ("output_budget", l.output_budget == Some(0)),
725 ];
726 for (field, zero) in zeros {
727 if zero {
728 return Err(invalid(field, ConfigReason::Zero));
729 }
730 }
731 if let Some(max) = l.max_connections {
732 if l.max_per_listener.is_some_and(|n| n > max) {
733 return Err(invalid("max_per_listener", ConfigReason::AboveGlobal));
734 }
735 if l.max_per_source.is_some_and(|(n, _)| n > max) {
736 return Err(invalid("max_per_source", ConfigReason::AboveGlobal));
737 }
738 }
739 if let Some(tick) = l.tick {
740 if tick < Duration::from_millis(1) {
741 return Err(invalid("tick", ConfigReason::TooSmall { min: 1 }));
742 }
743 if tick > Duration::from_millis(100) {
744 return Err(invalid("tick", ConfigReason::TooLarge { max: 100 }));
745 }
746 }
747 let tick = l.tick.unwrap_or(Duration::from_millis(1));
748 let timeouts: [(&'static str, Option<Duration>); 6] = [
749 ("handshake_timeout", l.handshake_timeout),
750 ("idle_timeout", l.idle_timeout),
751 ("read_timeout", l.read_timeout),
752 ("write_timeout", l.write_timeout),
753 ("park_timeout", l.park_timeout),
754 ("drain_timeout", l.drain_timeout),
755 ];
756 for (field, value) in timeouts {
757 if value.is_some_and(|d| d < tick) {
758 return Err(invalid(field, ConfigReason::BelowTick));
759 }
760 }
761 if let Some(rate) = l.min_rate {
762 if !rate.is_off() {
763 if rate.check.is_zero() || rate.grace.is_zero() {
764 return Err(invalid("min_rate", ConfigReason::Zero));
765 }
766 if rate.window < rate.check {
767 return Err(invalid("min_rate", ConfigReason::WindowBelowCheck));
768 }
769 }
770 }
771 if let Some((initial, max)) = l.read_class {
772 if max < initial {
773 return Err(invalid("read_class", ConfigReason::Inconsistent));
774 }
775 }
776 if l.refusal_bytes_max.is_some_and(|n| n > 512) {
777 return Err(invalid(
778 "refusal_bytes_max",
779 ConfigReason::TooLarge { max: 512 },
780 ));
781 }
782 if l.proxy_header_max.is_some_and(|n| n > 65_519) {
783 return Err(invalid(
784 "proxy_header_max",
785 ConfigReason::TooLarge { max: 65_519 },
786 ));
787 }
788 if l.completion_queue.is_some_and(|n| n > 1 << 20) {
789 return Err(invalid(
790 "completion_queue",
791 ConfigReason::TooLarge { max: 1 << 20 },
792 ));
793 }
794 if let Some(p) = l.pressure {
795 let factor_ok = p.factor > 0.0 && p.factor <= 1.0;
796 let ordered = p.high.partial_cmp(&p.low) == Some(std::cmp::Ordering::Greater);
797 if !ordered
798 || !factor_ok
799 || !(0.0..=1.0).contains(&p.high)
800 || !(0.0..=1.0).contains(&p.low)
801 {
802 return Err(invalid("pressure", ConfigReason::Inconsistent));
803 }
804 }
805 if l.lane_reserve[RESERVED_LANES - 1] == Some(u32::MAX) {
806 return Err(invalid("lane_reserve", ConfigReason::TooLarge { max: 7 }));
807 }
808 let reserved: u64 = l.lane_reserve.iter().flatten().map(|n| u64::from(*n)).sum();
809 if let Some(max) = l.max_connections {
810 if reserved > u64::from(max) {
811 return Err(invalid("lane_reserve", ConfigReason::AboveGlobal));
812 }
813 }
814 Ok(())
815 }
816}
817
818#[cfg(test)]
819mod tests {
820 use super::*;
821
822 #[test]
823 fn test_unset_values_take_driver_defaults() {
824 let l = Limits::builder().build().unwrap();
825 assert_eq!(
826 l.effective(DriverKind::Blocking).idle_timeout,
827 Duration::from_secs(5)
828 );
829 assert_eq!(
830 l.effective(DriverKind::Reactor).idle_timeout,
831 Duration::from_secs(60)
832 );
833 }
834
835 #[test]
836 fn test_dependent_defaults_follow_max_connections() {
837 let l = Limits::builder().max_connections(10).build().unwrap();
838 let e = l.effective(DriverKind::Reactor);
839 assert_eq!(e.max_per_listener, 10);
840 assert_eq!(e.max_per_source, 10);
841 }
842
843 #[test]
844 fn test_zero_values_rejected() {
845 for b in [
846 Limits::builder().max_connections(0),
847 Limits::builder().backlog(0),
848 Limits::builder().accept_batch(0),
849 Limits::builder().max_per_source(0, PrefixPolicy::DEFAULT),
850 Limits::builder().completion_queue(0),
851 Limits::builder().output_budget(0),
852 ] {
853 assert!(matches!(
854 b.build(),
855 Err(NetError::InvalidConfig {
856 reason: ConfigReason::Zero,
857 ..
858 })
859 ));
860 }
861 }
862
863 #[test]
864 fn test_timeout_below_tick_rejected() {
865 let err = Limits::builder()
866 .tick(Duration::from_millis(100))
867 .read_timeout(Duration::from_millis(50))
868 .build()
869 .unwrap_err();
870 assert!(matches!(
871 err,
872 NetError::InvalidConfig {
873 field: "read_timeout",
874 reason: ConfigReason::BelowTick
875 }
876 ));
877 }
878
879 #[test]
880 fn test_min_rate_window_below_check_rejected() {
881 let rate = MinRate {
882 bytes_per_sec: 10,
883 window: Duration::from_millis(500),
884 grace: Duration::from_secs(1),
885 check: Duration::from_secs(1),
886 };
887 assert!(matches!(
888 Limits::builder().min_rate(rate).build(),
889 Err(NetError::InvalidConfig {
890 reason: ConfigReason::WindowBelowCheck,
891 ..
892 })
893 ));
894 assert!(Limits::builder().min_rate(MinRate::off()).build().is_ok());
895 }
896
897 #[test]
898 fn test_bounds_too_large() {
899 assert!(Limits::builder().refusal_bytes_max(513).build().is_err());
900 assert!(Limits::builder().refusal_bytes_max(512).build().is_ok());
901 assert!(Limits::builder().proxy_header_max(65_520).build().is_err());
902 assert!(
903 Limits::builder()
904 .completion_queue((1 << 20) + 1)
905 .build()
906 .is_err()
907 );
908 }
909
910 #[test]
911 fn test_completion_queue_rounded_to_power_of_two() {
912 let l = Limits::builder().completion_queue(1000).build().unwrap();
913 assert_eq!(l.completion_queue(), Some(1024));
914 }
915
916 #[test]
917 fn test_pressure_and_lane_reserve_validation() {
918 let bad = Pressure {
919 high: 0.5,
920 low: 0.6,
921 factor: 0.5,
922 };
923 assert!(Limits::builder().pressure(bad).build().is_err());
924 assert!(Limits::builder().lane_reserve(Lane(8), 1).build().is_err());
925 assert!(
926 Limits::builder()
927 .max_connections(10)
928 .lane_reserve(Lane(0), 6)
929 .lane_reserve(Lane(1), 5)
930 .build()
931 .is_err()
932 );
933 }
934
935 #[test]
936 fn test_validate_descriptor_limit_and_budget() {
937 let l = Limits::builder().max_connections(1000).build().unwrap();
938 assert!(matches!(
939 l.validate(4, Some(500)),
940 Err(NetError::InvalidConfig {
941 reason: ConfigReason::DescriptorLimit { have: 500, .. },
942 ..
943 })
944 ));
945 assert!(l.validate(4, Some(100_000)).is_ok());
946 let small = Limits::builder().buffer_budget(1024).build().unwrap();
947 assert!(matches!(
948 small.validate(4, None),
949 Err(NetError::InvalidConfig {
950 reason: ConfigReason::BudgetBelowWorkers { .. },
951 ..
952 })
953 ));
954 }
955
956 #[test]
957 fn test_overlay_takes_listener_values() {
958 let base = Limits::builder()
959 .max_connections(100)
960 .idle_timeout(Duration::from_secs(9))
961 .build()
962 .unwrap();
963 let over = Limits::builder()
964 .idle_timeout(Duration::from_secs(1))
965 .build()
966 .unwrap();
967 let merged = base.overlay(&over);
968 assert_eq!(merged.max_connections(), Some(100));
969 assert_eq!(merged.idle_timeout(), Some(Duration::from_secs(1)));
970 }
971
972 #[test]
973 fn test_resolve_sets_every_value() {
974 let r = Limits::default().resolve(DriverKind::Blocking);
975 assert!(
976 r.max_connections().is_some() && r.tick().is_some() && r.graceful_linger().is_some()
977 );
978 }
979}