1use std::net::IpAddr;
8use std::path::PathBuf;
9
10use serde::{Deserialize, Serialize};
11
12use irontide_core::StorageMode;
13use irontide_wire::mse::EncryptionMode;
14
15use crate::alert::AlertCategory;
16use crate::choker::{ChokingAlgorithm, SeedChokingAlgorithm};
17use crate::proxy::ProxyConfig;
18use crate::rate_limiter::MixedModeAlgorithm;
19
20fn default_true() -> bool {
23 true
24}
25fn default_listen_port() -> u16 {
26 42020
27}
28fn default_download_dir() -> PathBuf {
29 PathBuf::from(".")
30}
31fn default_max_torrents() -> usize {
32 100
33}
34fn default_encryption() -> EncryptionMode {
35 EncryptionMode::Disabled
36}
37fn default_auto_upload_slots_min() -> usize {
38 2
39}
40fn default_auto_upload_slots_max() -> usize {
41 20
42}
43fn default_active_downloads() -> i32 {
44 3
45}
46fn default_active_seeds() -> i32 {
47 5
48}
49fn default_active_limit() -> i32 {
50 500
51}
52fn default_active_checking() -> i32 {
53 1
54}
55fn default_inactive_rate() -> u64 {
56 2048
57}
58fn default_auto_manage_interval() -> u64 {
59 30
60}
61fn default_auto_manage_startup() -> u64 {
62 60
63}
64fn default_alert_mask() -> AlertCategory {
65 AlertCategory::ALL
66}
67fn default_alert_channel_size() -> usize {
68 1024
69}
70fn default_smart_ban_max_failures() -> u32 {
71 3
72}
73fn default_disk_io_threads() -> usize {
74 let cores = std::thread::available_parallelism()
75 .map(|n| n.get())
76 .unwrap_or(4);
77 (cores / 2).clamp(4, 16)
78}
79fn default_max_blocking_threads() -> usize {
80 std::thread::available_parallelism()
81 .map(|n| n.get())
82 .unwrap_or(4)
83}
84fn default_storage_mode() -> StorageMode {
85 StorageMode::Auto
86}
87fn default_disk_cache_size() -> usize {
88 16 * 1024 * 1024
89}
90fn default_disk_write_cache_ratio() -> f32 {
91 0.5
92}
93fn default_buffer_pool_capacity() -> usize {
94 64 * 1024 * 1024
95}
96fn default_enable_mlock() -> bool {
97 cfg!(unix)
98}
99fn default_io_uring_sq_depth() -> u32 {
100 256
101}
102fn default_io_uring_batch_threshold() -> usize {
103 4
104}
105fn default_disk_channel_capacity() -> usize {
106 512
107}
108fn default_hashing_threads() -> usize {
109 let cores = std::thread::available_parallelism()
110 .map(|n| n.get())
111 .unwrap_or(4);
112 (cores / 4).clamp(2, 8)
113}
114fn default_max_request_queue_depth() -> usize {
115 250
116}
117fn default_initial_queue_depth() -> usize {
118 128
119}
120fn default_request_queue_time() -> f64 {
121 3.0
122}
123fn default_block_request_timeout() -> u32 {
124 60
125}
126fn default_max_concurrent_streams() -> usize {
127 8
128}
129fn default_dht_qps() -> usize {
130 50
131}
132fn default_dht_timeout() -> u64 {
133 5
134}
135fn default_upnp_lease() -> u32 {
136 3600
137}
138fn default_natpmp_lifetime() -> u32 {
139 7200
140}
141fn default_utp_max_conns() -> usize {
142 256
143}
144fn default_dht_max_items() -> usize {
145 700
146}
147fn default_dht_item_lifetime() -> u64 {
148 7200
149}
150fn default_dht_sample_interval() -> u64 {
151 0
152}
153fn default_max_suggest_pieces() -> usize {
154 16
155}
156fn default_predictive_piece_announce_ms() -> u64 {
157 0
158}
159fn default_ssl_listen_port() -> u16 {
160 0 }
162fn default_seed_choking_algorithm() -> SeedChokingAlgorithm {
163 SeedChokingAlgorithm::FastestUpload
164}
165fn default_choking_algorithm() -> ChokingAlgorithm {
166 ChokingAlgorithm::FixedSlots
167}
168fn default_mixed_mode() -> MixedModeAlgorithm {
169 MixedModeAlgorithm::PeerProportional
170}
171fn default_steal_threshold_ratio() -> f64 {
172 10.0
173}
174fn default_use_block_stealing() -> bool {
175 true
176}
177fn default_peer_connect_timeout() -> u64 {
178 10 }
180fn default_peer_dscp() -> u8 {
181 0x08 }
183fn default_max_peers_per_torrent() -> usize {
184 128
185}
186fn default_stats_report_interval() -> u64 {
187 1000
188}
189fn default_strict_end_game() -> bool {
190 true
191}
192fn default_max_web_seeds() -> usize {
193 4
194}
195fn default_initial_picker_threshold() -> u32 {
196 4
197}
198fn default_whole_pieces_threshold() -> u32 {
199 20
200}
201fn default_snub_timeout_secs() -> u32 {
202 15
203}
204fn default_readahead_pieces() -> u32 {
205 8
206}
207fn default_max_metadata_size() -> u64 {
208 4 * 1024 * 1024 }
210fn default_max_message_size() -> usize {
211 16 * 1024 * 1024 }
213fn default_max_piece_length() -> u64 {
214 32 * 1024 * 1024 }
216fn default_max_outstanding_requests() -> usize {
217 500
218}
219fn default_max_in_flight_pieces() -> usize {
220 512
221}
222fn default_fixed_pipeline_depth() -> usize {
223 128
224}
225fn default_i2p_hostname() -> String {
226 "127.0.0.1".into()
227}
228fn default_i2p_port() -> u16 {
229 7656
230}
231fn default_i2p_tunnel_quantity() -> u8 {
232 3
233}
234fn default_i2p_tunnel_length() -> u8 {
235 3
236}
237fn default_runtime_worker_threads() -> usize {
238 std::thread::available_parallelism()
239 .map(|n| n.get().min(8))
240 .unwrap_or(4)
241}
242fn default_lock_warn_threshold_ms() -> u64 {
243 50
244}
245fn default_steal_stale_piece_secs() -> u64 {
246 2
247}
248fn default_steal_threshold_endgame() -> f64 {
249 3.0
250}
251fn default_min_pipeline_depth() -> u32 {
252 16
253}
254fn default_max_pipeline_depth() -> u32 {
255 512
256}
257fn default_target_buffer_secs() -> f64 {
258 2.0
259}
260fn default_peer_read_timeout_secs() -> u64 {
261 10
262}
263fn default_peer_write_timeout_secs() -> u64 {
264 10
265}
266fn default_data_contribution_timeout() -> u64 {
267 0 }
269fn default_choke_rotation_max_evictions() -> u32 {
270 0 }
272fn default_max_concurrent_connects() -> u16 {
273 128 }
275fn default_connect_soft_timeout() -> u64 {
276 3 }
278fn default_save_resume_interval() -> u64 {
279 300 }
281
282#[derive(Debug, Clone, Serialize, Deserialize)]
290pub struct Settings {
291 #[serde(default = "default_listen_port")]
294 pub listen_port: u16,
295 #[serde(default = "default_download_dir")]
297 pub download_dir: PathBuf,
298 #[serde(default = "default_max_torrents")]
300 pub max_torrents: usize,
301 #[serde(default, skip_serializing_if = "Option::is_none")]
303 pub resume_data_dir: Option<PathBuf>,
304 #[serde(default = "default_save_resume_interval")]
307 pub save_resume_interval_secs: u64,
308
309 #[serde(default = "default_true")]
312 pub enable_dht: bool,
313 #[serde(default = "default_true")]
315 pub enable_pex: bool,
316 #[serde(default = "default_true")]
318 pub enable_lsd: bool,
319 #[serde(default = "default_true")]
322 pub enable_fast_extension: bool,
323 #[serde(default = "default_true")]
327 pub enable_utp: bool,
328 #[serde(default = "default_true")]
331 pub enable_upnp: bool,
332 #[serde(default = "default_true")]
335 pub enable_natpmp: bool,
336 #[serde(default = "default_true")]
340 pub enable_ipv6: bool,
341 #[serde(default = "default_true")]
345 pub enable_web_seed: bool,
346 #[serde(default = "default_true")]
350 pub enable_holepunch: bool,
351 #[serde(default = "default_true")]
355 pub enable_bep40_eviction: bool,
356 #[serde(default = "default_encryption")]
358 pub encryption_mode: EncryptionMode,
359 #[serde(default)]
362 pub anonymous_mode: bool,
363 #[serde(default, skip_serializing_if = "Option::is_none")]
366 pub external_ip: Option<IpAddr>,
367
368 #[serde(default, skip_serializing_if = "Option::is_none")]
371 pub seed_ratio_limit: Option<f64>,
372 #[serde(default)]
375 pub default_super_seeding: bool,
376 #[serde(default)]
379 pub default_share_mode: bool,
380 #[serde(default = "default_true")]
383 pub upload_only_announce: bool,
384 #[serde(default)]
387 pub upload_rate_limit: u64,
388 #[serde(default)]
390 pub download_rate_limit: u64,
391 #[serde(default)]
393 pub tcp_upload_rate_limit: u64,
394 #[serde(default)]
396 pub tcp_download_rate_limit: u64,
397 #[serde(default)]
399 pub utp_upload_rate_limit: u64,
400 #[serde(default)]
402 pub utp_download_rate_limit: u64,
403 #[serde(default = "default_true")]
405 pub auto_upload_slots: bool,
406 #[serde(default = "default_auto_upload_slots_min")]
408 pub auto_upload_slots_min: usize,
409 #[serde(default = "default_auto_upload_slots_max")]
411 pub auto_upload_slots_max: usize,
412 #[serde(default = "default_mixed_mode")]
414 pub mixed_mode_algorithm: MixedModeAlgorithm,
415
416 #[serde(default = "default_active_downloads")]
419 pub active_downloads: i32,
420 #[serde(default = "default_active_seeds")]
422 pub active_seeds: i32,
423 #[serde(default = "default_active_limit")]
425 pub active_limit: i32,
426 #[serde(default = "default_active_checking")]
428 pub active_checking: i32,
429 #[serde(default = "default_true")]
432 pub dont_count_slow_torrents: bool,
433 #[serde(default = "default_inactive_rate")]
436 pub inactive_down_rate: u64,
437 #[serde(default = "default_inactive_rate")]
440 pub inactive_up_rate: u64,
441 #[serde(default = "default_auto_manage_interval")]
443 pub auto_manage_interval: u64,
444 #[serde(default = "default_auto_manage_startup")]
447 pub auto_manage_startup: u64,
448 #[serde(default)]
450 pub auto_manage_prefer_seeds: bool,
451
452 #[serde(default = "default_alert_mask")]
455 pub alert_mask: AlertCategory,
456 #[serde(default = "default_alert_channel_size")]
458 pub alert_channel_size: usize,
459
460 #[serde(default = "default_smart_ban_max_failures")]
464 pub smart_ban_max_failures: u32,
465 #[serde(default = "default_true")]
468 pub smart_ban_parole: bool,
469
470 #[serde(default = "default_disk_io_threads")]
473 pub disk_io_threads: usize,
474 #[serde(default = "default_max_blocking_threads")]
477 pub max_blocking_threads: usize,
478 #[serde(default = "default_storage_mode")]
480 pub storage_mode: StorageMode,
481 #[serde(default, skip_serializing_if = "Option::is_none")]
484 pub preallocate_mode: Option<irontide_storage::PreallocateMode>,
485 #[serde(default = "default_disk_cache_size")]
487 pub disk_cache_size: usize,
488 #[serde(default = "default_disk_write_cache_ratio")]
490 pub disk_write_cache_ratio: f32,
491 #[serde(default = "default_disk_channel_capacity")]
493 pub disk_channel_capacity: usize,
494 #[serde(default = "default_buffer_pool_capacity")]
497 pub buffer_pool_capacity: usize,
498 #[serde(default = "default_enable_mlock")]
502 pub enable_mlock: bool,
503 #[serde(default = "default_io_uring_sq_depth")]
506 pub io_uring_sq_depth: u32,
507 #[serde(default)]
510 pub io_uring_direct_io: bool,
511 #[serde(default)]
515 pub filesystem_direct_io: bool,
516 #[serde(default = "default_io_uring_batch_threshold")]
519 pub io_uring_batch_threshold: usize,
520 #[serde(default)]
523 pub iocp_concurrent_threads: u32,
524 #[serde(default)]
527 pub iocp_direct_io: bool,
528 #[serde(default = "default_hashing_threads")]
531 pub hashing_threads: usize,
532 #[serde(default = "default_max_request_queue_depth")]
534 pub max_request_queue_depth: usize,
535 #[serde(default = "default_initial_queue_depth")]
538 pub initial_queue_depth: usize,
539 #[serde(default = "default_request_queue_time")]
546 pub request_queue_time: f64,
547 #[serde(default = "default_block_request_timeout")]
550 pub block_request_timeout_secs: u32,
551 #[serde(default = "default_max_concurrent_streams")]
554 pub max_concurrent_stream_reads: usize,
555 #[serde(default = "default_true")]
558 pub auto_sequential: bool,
559 #[serde(default = "default_strict_end_game")]
563 pub strict_end_game: bool,
564 #[serde(default = "default_max_web_seeds")]
566 pub max_web_seeds: usize,
567 #[serde(default = "default_initial_picker_threshold")]
570 pub initial_picker_threshold: u32,
571 #[serde(default = "default_whole_pieces_threshold")]
574 pub whole_pieces_threshold: u32,
575 #[serde(default = "default_snub_timeout_secs")]
578 pub snub_timeout_secs: u32,
579 #[serde(default = "default_readahead_pieces")]
581 pub readahead_pieces: u32,
582 #[serde(default = "default_true")]
584 pub streaming_timeout_escalation: bool,
585 #[serde(default = "default_steal_threshold_ratio")]
588 pub steal_threshold_ratio: f64,
589 #[serde(default = "default_use_block_stealing")]
592 pub use_block_stealing: bool,
593 #[serde(default = "default_steal_stale_piece_secs")]
598 pub steal_stale_piece_secs: u64,
599 #[serde(default = "default_steal_threshold_endgame")]
602 pub steal_threshold_endgame: f64,
603 #[serde(default = "default_min_pipeline_depth")]
605 pub min_pipeline_depth: u32,
606 #[serde(default = "default_max_pipeline_depth")]
608 pub max_pipeline_depth: u32,
609 #[serde(default = "default_target_buffer_secs")]
612 pub target_buffer_secs: f64,
613 #[serde(default = "default_fixed_pipeline_depth")]
618 pub fixed_pipeline_depth: usize,
619
620 #[serde(default = "default_true")]
624 pub piece_extent_affinity: bool,
625 #[serde(default)]
628 pub suggest_mode: bool,
629 #[serde(default = "default_max_suggest_pieces")]
631 pub max_suggest_pieces: usize,
632 #[serde(default = "default_predictive_piece_announce_ms")]
636 pub predictive_piece_announce_ms: u64,
637
638 #[serde(default)]
641 pub proxy: ProxyConfig,
642 #[serde(default)]
645 pub force_proxy: bool,
646 #[serde(default = "default_true")]
649 pub apply_ip_filter_to_trackers: bool,
650
651 #[serde(default = "default_dht_qps")]
654 pub dht_queries_per_second: usize,
655 #[serde(default = "default_dht_timeout")]
657 pub dht_query_timeout_secs: u64,
658 #[serde(default)]
661 pub dht_enforce_node_id: bool,
662 #[serde(default = "default_true")]
664 pub dht_restrict_routing_ips: bool,
665 #[serde(default = "default_dht_max_items")]
667 pub dht_max_items: usize,
668 #[serde(default = "default_dht_item_lifetime")]
670 pub dht_item_lifetime_secs: u64,
671 #[serde(default = "default_dht_sample_interval")]
674 pub dht_sample_infohashes_interval: u64,
675 #[serde(default)]
679 pub dht_read_only: bool,
680
681 #[serde(default = "default_upnp_lease")]
684 pub upnp_lease_duration: u32,
685 #[serde(default = "default_natpmp_lifetime")]
687 pub natpmp_lifetime: u32,
688
689 #[serde(default = "default_utp_max_conns")]
692 pub utp_max_connections: usize,
693
694 #[serde(default)]
697 pub enable_i2p: bool,
698 #[serde(default = "default_i2p_hostname")]
700 pub i2p_hostname: String,
701 #[serde(default = "default_i2p_port")]
703 pub i2p_port: u16,
704 #[serde(default = "default_i2p_tunnel_quantity")]
706 pub i2p_inbound_quantity: u8,
707 #[serde(default = "default_i2p_tunnel_quantity")]
709 pub i2p_outbound_quantity: u8,
710 #[serde(default = "default_i2p_tunnel_length")]
712 pub i2p_inbound_length: u8,
713 #[serde(default = "default_i2p_tunnel_length")]
715 pub i2p_outbound_length: u8,
716 #[serde(default)]
719 pub allow_i2p_mixed: bool,
720
721 #[serde(default = "default_ssl_listen_port")]
726 pub ssl_listen_port: u16,
727 #[serde(default, skip_serializing_if = "Option::is_none")]
731 pub ssl_cert_path: Option<PathBuf>,
732 #[serde(default, skip_serializing_if = "Option::is_none")]
734 pub ssl_key_path: Option<PathBuf>,
735
736 #[serde(default = "default_seed_choking_algorithm")]
739 pub seed_choking_algorithm: SeedChokingAlgorithm,
740 #[serde(default = "default_choking_algorithm")]
742 pub choking_algorithm: ChokingAlgorithm,
743
744 #[serde(default = "default_max_peers_per_torrent")]
747 pub max_peers_per_torrent: usize,
748
749 #[serde(default = "default_peer_read_timeout_secs")]
752 pub peer_read_timeout_secs: u64,
753 #[serde(default = "default_peer_write_timeout_secs")]
756 pub peer_write_timeout_secs: u64,
757
758 #[serde(default = "default_data_contribution_timeout")]
761 pub data_contribution_timeout_secs: u64,
762
763 #[serde(default = "default_choke_rotation_max_evictions")]
765 pub choke_rotation_max_evictions: u32,
766
767 #[serde(default = "default_max_concurrent_connects")]
769 pub max_concurrent_connects: u16,
770
771 #[serde(default = "default_connect_soft_timeout")]
774 pub connect_soft_timeout: u64,
775
776 #[serde(default = "default_true")]
780 pub ssrf_mitigation: bool,
781 #[serde(default)]
783 pub allow_idna: bool,
784 #[serde(default = "default_true")]
786 pub validate_https_trackers: bool,
787 #[serde(default = "default_max_metadata_size")]
790 pub max_metadata_size: u64,
791 #[serde(default = "default_max_message_size")]
794 pub max_message_size: usize,
795 #[serde(default = "default_max_piece_length")]
798 pub max_piece_length: u64,
799 #[serde(default = "default_max_outstanding_requests")]
803 pub max_outstanding_requests: usize,
804 #[serde(default = "default_max_in_flight_pieces")]
809 pub max_in_flight_pieces: usize,
810 #[serde(default = "default_peer_connect_timeout")]
813 pub peer_connect_timeout: u64,
814 #[serde(default = "default_peer_dscp")]
818 pub peer_dscp: u8,
819
820 #[serde(default = "default_stats_report_interval")]
824 pub stats_report_interval: u64,
825
826 #[serde(default = "default_runtime_worker_threads")]
830 pub runtime_worker_threads: usize,
831 #[serde(default = "default_true")]
833 pub pin_cores: bool,
834
835 #[serde(default = "default_lock_warn_threshold_ms")]
841 pub lock_warn_threshold_ms: u64,
842
843 #[serde(skip)]
849 pub dht_saved_nodes: Vec<String>,
850 #[serde(skip)]
854 pub dht_node_id: Option<irontide_core::Id20>,
855}
856
857impl Default for Settings {
858 fn default() -> Self {
859 Self {
860 listen_port: 42020,
862 download_dir: PathBuf::from("."),
863 max_torrents: 100,
864 resume_data_dir: None,
865 save_resume_interval_secs: 300,
866 enable_dht: true,
868 enable_pex: true,
869 enable_lsd: true,
870 enable_fast_extension: true,
871 enable_utp: true,
872 enable_upnp: true,
873 enable_natpmp: true,
874 enable_ipv6: true,
875 enable_web_seed: true,
876 enable_holepunch: true,
877 enable_bep40_eviction: true,
878 encryption_mode: EncryptionMode::Disabled,
879 anonymous_mode: false,
880 external_ip: None,
881 seed_ratio_limit: None,
883 default_super_seeding: false,
884 default_share_mode: false,
885 upload_only_announce: true,
886 upload_rate_limit: 0,
888 download_rate_limit: 0,
889 tcp_upload_rate_limit: 0,
890 tcp_download_rate_limit: 0,
891 utp_upload_rate_limit: 0,
892 utp_download_rate_limit: 0,
893 auto_upload_slots: true,
894 auto_upload_slots_min: 2,
895 auto_upload_slots_max: 20,
896 mixed_mode_algorithm: MixedModeAlgorithm::PeerProportional,
897 active_downloads: 3,
899 active_seeds: 5,
900 active_limit: 500,
901 active_checking: 1,
902 dont_count_slow_torrents: true,
903 inactive_down_rate: 2048,
904 inactive_up_rate: 2048,
905 auto_manage_interval: 30,
906 auto_manage_startup: 60,
907 auto_manage_prefer_seeds: false,
908 alert_mask: AlertCategory::ALL,
910 alert_channel_size: 1024,
911 smart_ban_max_failures: 3,
913 smart_ban_parole: true,
914 disk_io_threads: default_disk_io_threads(),
916 max_blocking_threads: default_max_blocking_threads(),
917 storage_mode: StorageMode::Auto,
918 preallocate_mode: None,
919 disk_cache_size: 16 * 1024 * 1024,
920 disk_write_cache_ratio: 0.5,
921 disk_channel_capacity: 512,
922 buffer_pool_capacity: 64 * 1024 * 1024,
923 enable_mlock: cfg!(unix),
924 io_uring_sq_depth: 256,
925 io_uring_direct_io: false,
926 filesystem_direct_io: false,
927 io_uring_batch_threshold: 4,
928 iocp_concurrent_threads: 0,
929 iocp_direct_io: false,
930 hashing_threads: default_hashing_threads(),
932 max_request_queue_depth: 250,
933 initial_queue_depth: 128,
934 request_queue_time: 3.0,
935 block_request_timeout_secs: 60,
936 max_concurrent_stream_reads: 8,
937 auto_sequential: true,
938 steal_threshold_ratio: 10.0,
939 use_block_stealing: true,
940 steal_stale_piece_secs: 2,
941 steal_threshold_endgame: 3.0,
942 min_pipeline_depth: 16,
943 max_pipeline_depth: 512,
944 target_buffer_secs: 2.0,
945 fixed_pipeline_depth: 128,
946 strict_end_game: true,
947 max_web_seeds: 4,
948 initial_picker_threshold: 4,
949 whole_pieces_threshold: 20,
950 snub_timeout_secs: 15,
951 readahead_pieces: 8,
952 streaming_timeout_escalation: true,
953 piece_extent_affinity: true,
955 suggest_mode: false,
956 max_suggest_pieces: 16,
957 predictive_piece_announce_ms: 0,
958 proxy: ProxyConfig::default(),
960 force_proxy: false,
961 apply_ip_filter_to_trackers: true,
962 dht_queries_per_second: 50,
964 dht_query_timeout_secs: 5,
965 dht_enforce_node_id: false,
966 dht_restrict_routing_ips: true,
967 dht_max_items: 700,
968 dht_item_lifetime_secs: 7200,
969 dht_sample_infohashes_interval: 0,
970 dht_read_only: false,
971 upnp_lease_duration: 3600,
973 natpmp_lifetime: 7200,
974 utp_max_connections: 256,
976 enable_i2p: false,
978 i2p_hostname: "127.0.0.1".into(),
979 i2p_port: 7656,
980 i2p_inbound_quantity: 3,
981 i2p_outbound_quantity: 3,
982 i2p_inbound_length: 3,
983 i2p_outbound_length: 3,
984 allow_i2p_mixed: false,
985 ssl_listen_port: 0,
987 ssl_cert_path: None,
988 ssl_key_path: None,
989 seed_choking_algorithm: SeedChokingAlgorithm::FastestUpload,
991 choking_algorithm: ChokingAlgorithm::FixedSlots,
992 max_peers_per_torrent: 128,
994 peer_read_timeout_secs: 10,
995 peer_write_timeout_secs: 10,
996 data_contribution_timeout_secs: 0,
997 choke_rotation_max_evictions: 0,
998 max_concurrent_connects: 128,
999 connect_soft_timeout: 3,
1000 ssrf_mitigation: true,
1002 allow_idna: false,
1003 validate_https_trackers: true,
1004 max_metadata_size: 4 * 1024 * 1024,
1005 max_message_size: 16 * 1024 * 1024,
1006 max_piece_length: 32 * 1024 * 1024,
1007 max_outstanding_requests: 500,
1008 max_in_flight_pieces: 512,
1009 peer_connect_timeout: 10,
1010 peer_dscp: 0x08,
1011 stats_report_interval: 1000,
1013 runtime_worker_threads: default_runtime_worker_threads(),
1015 pin_cores: true,
1016 lock_warn_threshold_ms: 50,
1018 dht_saved_nodes: Vec::new(),
1020 dht_node_id: None,
1021 }
1022 }
1023}
1024
1025impl Settings {
1026 pub fn min_memory() -> Self {
1028 Self {
1029 disk_cache_size: 8 * 1024 * 1024,
1030 buffer_pool_capacity: 16 * 1024 * 1024,
1031 max_torrents: 20,
1032 max_peers_per_torrent: 30,
1033 active_downloads: 1,
1034 active_seeds: 2,
1035 active_limit: 10,
1036 alert_channel_size: 256,
1037 utp_max_connections: 64,
1038 max_request_queue_depth: 50,
1039 initial_queue_depth: 16,
1040 max_concurrent_stream_reads: 2,
1041 hashing_threads: 1,
1042 disk_io_threads: 1,
1043 dht_max_items: 100,
1044 max_in_flight_pieces: 32,
1045 fixed_pipeline_depth: 32,
1046 ..Self::default()
1047 }
1048 }
1049
1050 pub fn high_performance() -> Self {
1052 Self {
1053 disk_cache_size: 256 * 1024 * 1024,
1054 buffer_pool_capacity: 256 * 1024 * 1024,
1055 max_torrents: 2000,
1056 max_peers_per_torrent: 200,
1057 active_downloads: 30,
1058 active_seeds: 100,
1059 active_limit: 2000,
1060 alert_channel_size: 4096,
1061 utp_max_connections: 1024,
1062 max_request_queue_depth: 1000,
1063 initial_queue_depth: 256,
1064 max_concurrent_stream_reads: 32,
1065 hashing_threads: 4,
1066 disk_io_threads: 8,
1067 auto_upload_slots_max: 100,
1068 suggest_mode: true,
1069 steal_threshold_ratio: 5.0,
1070 steal_threshold_endgame: 2.0,
1071 min_pipeline_depth: 16,
1072 max_pipeline_depth: 512,
1073 target_buffer_secs: 2.0,
1074 use_block_stealing: true,
1075 max_in_flight_pieces: 512,
1076 ..Self::default()
1077 }
1078 }
1079
1080 pub fn validate(&self) -> crate::Result<()> {
1082 use crate::proxy::ProxyType;
1083
1084 if self.force_proxy && self.proxy.proxy_type == ProxyType::None {
1085 return Err(crate::Error::InvalidSettings(
1086 "force_proxy is enabled but no proxy type is configured".into(),
1087 ));
1088 }
1089
1090 if self.active_downloads > 0
1091 && self.active_limit > 0
1092 && self.active_downloads > self.active_limit
1093 {
1094 return Err(crate::Error::InvalidSettings(
1095 "active_downloads exceeds active_limit".into(),
1096 ));
1097 }
1098
1099 if self.active_seeds > 0 && self.active_limit > 0 && self.active_seeds > self.active_limit {
1100 return Err(crate::Error::InvalidSettings(
1101 "active_seeds exceeds active_limit".into(),
1102 ));
1103 }
1104
1105 if !(0.0..=1.0).contains(&self.disk_write_cache_ratio) {
1106 return Err(crate::Error::InvalidSettings(
1107 "disk_write_cache_ratio must be between 0.0 and 1.0".into(),
1108 ));
1109 }
1110
1111 if self.disk_cache_size < 1024 * 1024 {
1112 return Err(crate::Error::InvalidSettings(
1113 "disk_cache_size must be at least 1 MiB".into(),
1114 ));
1115 }
1116
1117 if self.hashing_threads == 0 {
1118 return Err(crate::Error::InvalidSettings(
1119 "hashing_threads must be at least 1".into(),
1120 ));
1121 }
1122
1123 if self.disk_io_threads == 0 {
1124 return Err(crate::Error::InvalidSettings(
1125 "disk_io_threads must be at least 1".into(),
1126 ));
1127 }
1128
1129 if self.max_blocking_threads == 0 {
1130 return Err(crate::Error::InvalidSettings(
1131 "max_blocking_threads must be at least 1".into(),
1132 ));
1133 }
1134
1135 if self.default_share_mode && !self.enable_fast_extension {
1136 return Err(crate::Error::InvalidSettings(
1137 "share_mode requires enable_fast_extension for RejectRequest messages".into(),
1138 ));
1139 }
1140
1141 if self.ssl_cert_path.is_some() != self.ssl_key_path.is_some() {
1143 return Err(crate::Error::InvalidSettings(
1144 "ssl_cert_path and ssl_key_path must both be set or both absent".into(),
1145 ));
1146 }
1147
1148 if self.enable_i2p {
1149 if self.i2p_inbound_quantity == 0 || self.i2p_inbound_quantity > 16 {
1150 return Err(crate::Error::InvalidSettings(
1151 "i2p_inbound_quantity must be 1-16".into(),
1152 ));
1153 }
1154 if self.i2p_outbound_quantity == 0 || self.i2p_outbound_quantity > 16 {
1155 return Err(crate::Error::InvalidSettings(
1156 "i2p_outbound_quantity must be 1-16".into(),
1157 ));
1158 }
1159 if self.i2p_inbound_length > 7 {
1160 return Err(crate::Error::InvalidSettings(
1161 "i2p_inbound_length must be 0-7".into(),
1162 ));
1163 }
1164 if self.i2p_outbound_length > 7 {
1165 return Err(crate::Error::InvalidSettings(
1166 "i2p_outbound_length must be 0-7".into(),
1167 ));
1168 }
1169 }
1170
1171 if self.runtime_worker_threads > 256 {
1172 return Err(crate::Error::InvalidSettings(
1173 "runtime_worker_threads must be at most 256".into(),
1174 ));
1175 }
1176
1177 Ok(())
1178 }
1179}
1180
1181impl From<&Settings> for crate::disk::DiskConfig {
1184 fn from(s: &Settings) -> Self {
1185 Self {
1186 io_threads: s.disk_io_threads,
1187 storage_mode: s.storage_mode,
1188 cache_size: s.disk_cache_size,
1189 write_cache_ratio: s.disk_write_cache_ratio,
1190 channel_capacity: s.disk_channel_capacity,
1191 buffer_pool_capacity: s.buffer_pool_capacity,
1192 enable_mlock: s.enable_mlock,
1193 lock_warn_threshold_ms: s.lock_warn_threshold_ms,
1194 io_uring_sq_depth: s.io_uring_sq_depth,
1195 io_uring_direct_io: s.io_uring_direct_io,
1196 filesystem_direct_io: s.filesystem_direct_io,
1197 io_uring_batch_threshold: s.io_uring_batch_threshold,
1198 iocp_concurrent_threads: s.iocp_concurrent_threads,
1199 iocp_direct_io: s.iocp_direct_io,
1200 }
1201 }
1202}
1203
1204impl From<&Settings> for crate::ban::BanConfig {
1205 fn from(s: &Settings) -> Self {
1206 Self {
1207 max_failures: s.smart_ban_max_failures,
1208 use_parole: s.smart_ban_parole,
1209 }
1210 }
1211}
1212
1213impl Settings {
1214 pub(crate) fn to_dht_config(&self) -> irontide_dht::DhtConfig {
1215 let default = irontide_dht::DhtConfig::default();
1216 let mut bootstrap = self.dht_saved_nodes.clone();
1217 bootstrap.extend(default.bootstrap_nodes.iter().cloned());
1218 irontide_dht::DhtConfig {
1219 bootstrap_nodes: bootstrap,
1220 own_id: self.dht_node_id,
1221 queries_per_second: self.dht_queries_per_second,
1222 query_timeout: std::time::Duration::from_secs(self.dht_query_timeout_secs),
1223 enforce_node_id: self.dht_enforce_node_id,
1224 restrict_routing_ips: self.dht_restrict_routing_ips,
1225 dht_max_items: self.dht_max_items,
1226 dht_item_lifetime_secs: self.dht_item_lifetime_secs,
1227 state_dir: self.resume_data_dir.clone(),
1228 read_only_mode: self.dht_read_only,
1229 ..default
1230 }
1231 }
1232
1233 pub(crate) fn to_dht_config_v6(&self) -> irontide_dht::DhtConfig {
1234 let default = irontide_dht::DhtConfig::default_v6();
1235 let mut bootstrap = self.dht_saved_nodes.clone();
1236 bootstrap.extend(default.bootstrap_nodes.iter().cloned());
1237 irontide_dht::DhtConfig {
1238 bootstrap_nodes: bootstrap,
1239 queries_per_second: self.dht_queries_per_second,
1240 query_timeout: std::time::Duration::from_secs(self.dht_query_timeout_secs),
1241 enforce_node_id: self.dht_enforce_node_id,
1242 restrict_routing_ips: self.dht_restrict_routing_ips,
1243 dht_max_items: self.dht_max_items,
1244 dht_item_lifetime_secs: self.dht_item_lifetime_secs,
1245 state_dir: self.resume_data_dir.clone(),
1246 read_only_mode: self.dht_read_only,
1247 ..default
1248 }
1249 }
1250
1251 pub(crate) fn to_nat_config(&self) -> irontide_nat::NatConfig {
1252 irontide_nat::NatConfig {
1253 enable_upnp: self.enable_upnp,
1254 enable_natpmp: self.enable_natpmp,
1255 upnp_lease_duration: self.upnp_lease_duration,
1256 natpmp_lifetime: self.natpmp_lifetime,
1257 }
1258 }
1259
1260 pub(crate) fn to_utp_config(&self, port: u16) -> irontide_utp::UtpConfig {
1261 irontide_utp::UtpConfig {
1262 bind_addr: std::net::SocketAddr::from(([0, 0, 0, 0], port)),
1263 max_connections: self.utp_max_connections,
1264 dscp: self.peer_dscp,
1265 }
1266 }
1267
1268 pub(crate) fn to_utp_config_v6(&self, port: u16) -> irontide_utp::UtpConfig {
1269 irontide_utp::UtpConfig {
1270 bind_addr: std::net::SocketAddr::from((std::net::Ipv6Addr::UNSPECIFIED, port)),
1271 max_connections: self.utp_max_connections,
1272 dscp: self.peer_dscp,
1273 }
1274 }
1275
1276 pub(crate) fn to_sam_tunnel_config(&self) -> crate::i2p::SamTunnelConfig {
1278 crate::i2p::SamTunnelConfig {
1279 inbound_quantity: self.i2p_inbound_quantity,
1280 outbound_quantity: self.i2p_outbound_quantity,
1281 inbound_length: self.i2p_inbound_length,
1282 outbound_length: self.i2p_outbound_length,
1283 }
1284 }
1285}
1286
1287impl PartialEq for Settings {
1290 fn eq(&self, other: &Self) -> bool {
1291 self.listen_port == other.listen_port
1292 && self.download_dir == other.download_dir
1293 && self.max_torrents == other.max_torrents
1294 && self.resume_data_dir == other.resume_data_dir
1295 && self.save_resume_interval_secs == other.save_resume_interval_secs
1296 && self.enable_dht == other.enable_dht
1297 && self.enable_pex == other.enable_pex
1298 && self.enable_lsd == other.enable_lsd
1299 && self.enable_fast_extension == other.enable_fast_extension
1300 && self.enable_utp == other.enable_utp
1301 && self.enable_upnp == other.enable_upnp
1302 && self.enable_natpmp == other.enable_natpmp
1303 && self.enable_ipv6 == other.enable_ipv6
1304 && self.enable_web_seed == other.enable_web_seed
1305 && self.enable_holepunch == other.enable_holepunch
1306 && self.enable_bep40_eviction == other.enable_bep40_eviction
1307 && self.encryption_mode == other.encryption_mode
1308 && self.anonymous_mode == other.anonymous_mode
1309 && self.external_ip == other.external_ip
1310 && self.seed_ratio_limit == other.seed_ratio_limit
1311 && self.default_super_seeding == other.default_super_seeding
1312 && self.default_share_mode == other.default_share_mode
1313 && self.upload_only_announce == other.upload_only_announce
1314 && self.upload_rate_limit == other.upload_rate_limit
1315 && self.download_rate_limit == other.download_rate_limit
1316 && self.tcp_upload_rate_limit == other.tcp_upload_rate_limit
1317 && self.tcp_download_rate_limit == other.tcp_download_rate_limit
1318 && self.utp_upload_rate_limit == other.utp_upload_rate_limit
1319 && self.utp_download_rate_limit == other.utp_download_rate_limit
1320 && self.auto_upload_slots == other.auto_upload_slots
1321 && self.auto_upload_slots_min == other.auto_upload_slots_min
1322 && self.auto_upload_slots_max == other.auto_upload_slots_max
1323 && self.mixed_mode_algorithm == other.mixed_mode_algorithm
1324 && self.active_downloads == other.active_downloads
1325 && self.active_seeds == other.active_seeds
1326 && self.active_limit == other.active_limit
1327 && self.active_checking == other.active_checking
1328 && self.dont_count_slow_torrents == other.dont_count_slow_torrents
1329 && self.inactive_down_rate == other.inactive_down_rate
1330 && self.inactive_up_rate == other.inactive_up_rate
1331 && self.auto_manage_interval == other.auto_manage_interval
1332 && self.auto_manage_startup == other.auto_manage_startup
1333 && self.auto_manage_prefer_seeds == other.auto_manage_prefer_seeds
1334 && self.alert_mask == other.alert_mask
1335 && self.alert_channel_size == other.alert_channel_size
1336 && self.smart_ban_max_failures == other.smart_ban_max_failures
1337 && self.smart_ban_parole == other.smart_ban_parole
1338 && self.disk_io_threads == other.disk_io_threads
1339 && self.max_blocking_threads == other.max_blocking_threads
1340 && self.storage_mode == other.storage_mode
1341 && self.disk_cache_size == other.disk_cache_size
1342 && self.disk_write_cache_ratio.to_bits() == other.disk_write_cache_ratio.to_bits()
1343 && self.disk_channel_capacity == other.disk_channel_capacity
1344 && self.buffer_pool_capacity == other.buffer_pool_capacity
1345 && self.enable_mlock == other.enable_mlock
1346 && self.hashing_threads == other.hashing_threads
1347 && self.max_request_queue_depth == other.max_request_queue_depth
1348 && self.initial_queue_depth == other.initial_queue_depth
1349 && self.request_queue_time.to_bits() == other.request_queue_time.to_bits()
1350 && self.block_request_timeout_secs == other.block_request_timeout_secs
1351 && self.max_concurrent_stream_reads == other.max_concurrent_stream_reads
1352 && self.auto_sequential == other.auto_sequential
1353 && self.steal_threshold_ratio.to_bits() == other.steal_threshold_ratio.to_bits()
1354 && self.use_block_stealing == other.use_block_stealing
1355 && self.steal_stale_piece_secs == other.steal_stale_piece_secs
1356 && self.steal_threshold_endgame.to_bits() == other.steal_threshold_endgame.to_bits()
1357 && self.min_pipeline_depth == other.min_pipeline_depth
1358 && self.max_pipeline_depth == other.max_pipeline_depth
1359 && self.target_buffer_secs.to_bits() == other.target_buffer_secs.to_bits()
1360 && self.fixed_pipeline_depth == other.fixed_pipeline_depth
1361 && self.strict_end_game == other.strict_end_game
1362 && self.max_web_seeds == other.max_web_seeds
1363 && self.initial_picker_threshold == other.initial_picker_threshold
1364 && self.whole_pieces_threshold == other.whole_pieces_threshold
1365 && self.snub_timeout_secs == other.snub_timeout_secs
1366 && self.readahead_pieces == other.readahead_pieces
1367 && self.streaming_timeout_escalation == other.streaming_timeout_escalation
1368 && self.piece_extent_affinity == other.piece_extent_affinity
1369 && self.suggest_mode == other.suggest_mode
1370 && self.max_suggest_pieces == other.max_suggest_pieces
1371 && self.predictive_piece_announce_ms == other.predictive_piece_announce_ms
1372 && self.force_proxy == other.force_proxy
1373 && self.apply_ip_filter_to_trackers == other.apply_ip_filter_to_trackers
1374 && self.dht_queries_per_second == other.dht_queries_per_second
1375 && self.dht_query_timeout_secs == other.dht_query_timeout_secs
1376 && self.dht_enforce_node_id == other.dht_enforce_node_id
1377 && self.dht_restrict_routing_ips == other.dht_restrict_routing_ips
1378 && self.dht_max_items == other.dht_max_items
1379 && self.dht_item_lifetime_secs == other.dht_item_lifetime_secs
1380 && self.dht_sample_infohashes_interval == other.dht_sample_infohashes_interval
1381 && self.dht_read_only == other.dht_read_only
1382 && self.upnp_lease_duration == other.upnp_lease_duration
1383 && self.natpmp_lifetime == other.natpmp_lifetime
1384 && self.utp_max_connections == other.utp_max_connections
1385 && self.enable_i2p == other.enable_i2p
1386 && self.i2p_hostname == other.i2p_hostname
1387 && self.i2p_port == other.i2p_port
1388 && self.i2p_inbound_quantity == other.i2p_inbound_quantity
1389 && self.i2p_outbound_quantity == other.i2p_outbound_quantity
1390 && self.i2p_inbound_length == other.i2p_inbound_length
1391 && self.i2p_outbound_length == other.i2p_outbound_length
1392 && self.allow_i2p_mixed == other.allow_i2p_mixed
1393 && self.ssl_listen_port == other.ssl_listen_port
1394 && self.ssl_cert_path == other.ssl_cert_path
1395 && self.ssl_key_path == other.ssl_key_path
1396 && self.seed_choking_algorithm == other.seed_choking_algorithm
1397 && self.choking_algorithm == other.choking_algorithm
1398 && self.max_peers_per_torrent == other.max_peers_per_torrent
1399 && self.peer_read_timeout_secs == other.peer_read_timeout_secs
1400 && self.peer_write_timeout_secs == other.peer_write_timeout_secs
1401 && self.data_contribution_timeout_secs == other.data_contribution_timeout_secs
1402 && self.choke_rotation_max_evictions == other.choke_rotation_max_evictions
1403 && self.max_concurrent_connects == other.max_concurrent_connects
1404 && self.connect_soft_timeout == other.connect_soft_timeout
1405 && self.ssrf_mitigation == other.ssrf_mitigation
1406 && self.allow_idna == other.allow_idna
1407 && self.validate_https_trackers == other.validate_https_trackers
1408 && self.max_metadata_size == other.max_metadata_size
1409 && self.max_message_size == other.max_message_size
1410 && self.max_piece_length == other.max_piece_length
1411 && self.max_outstanding_requests == other.max_outstanding_requests
1412 && self.max_in_flight_pieces == other.max_in_flight_pieces
1413 && self.peer_connect_timeout == other.peer_connect_timeout
1414 && self.peer_dscp == other.peer_dscp
1415 && self.stats_report_interval == other.stats_report_interval
1416 && self.runtime_worker_threads == other.runtime_worker_threads
1417 && self.pin_cores == other.pin_cores
1418 && self.dht_saved_nodes == other.dht_saved_nodes
1419 && self.dht_node_id == other.dht_node_id
1420 }
1421}
1422
1423#[cfg(test)]
1426mod tests {
1427 use super::*;
1428
1429 #[test]
1430 fn default_settings_values() {
1431 let s = Settings::default();
1432 assert_eq!(s.listen_port, 42020);
1433 assert_eq!(s.download_dir, PathBuf::from("."));
1434 assert_eq!(s.max_torrents, 100);
1435 assert!(s.resume_data_dir.is_none());
1436 assert_eq!(s.save_resume_interval_secs, 300);
1437 assert!(s.enable_dht);
1438 assert!(s.enable_pex);
1439 assert!(s.enable_lsd);
1440 assert!(s.enable_fast_extension);
1441 assert!(s.enable_utp);
1442 assert!(s.enable_upnp);
1443 assert!(s.enable_natpmp);
1444 assert!(s.enable_ipv6);
1445 assert!(s.enable_web_seed);
1446 assert_eq!(s.encryption_mode, EncryptionMode::Disabled);
1447 assert!(!s.anonymous_mode);
1448 assert!(s.seed_ratio_limit.is_none());
1449 assert!(!s.default_super_seeding);
1450 assert!(!s.default_share_mode);
1451 assert!(s.upload_only_announce);
1452 assert_eq!(s.upload_rate_limit, 0);
1453 assert_eq!(s.download_rate_limit, 0);
1454 assert!(s.auto_upload_slots);
1455 assert_eq!(s.active_downloads, 3);
1456 assert_eq!(s.active_seeds, 5);
1457 assert_eq!(s.active_limit, 500);
1458 assert_eq!(s.active_checking, 1);
1459 assert!(s.dont_count_slow_torrents);
1460 assert_eq!(s.alert_mask, AlertCategory::ALL);
1461 assert_eq!(s.alert_channel_size, 1024);
1462 assert_eq!(s.smart_ban_max_failures, 3);
1463 assert!(s.smart_ban_parole);
1464 assert_eq!(s.disk_io_threads, default_disk_io_threads());
1465 assert_eq!(s.max_blocking_threads, default_max_blocking_threads());
1466 assert_eq!(s.storage_mode, StorageMode::Auto);
1467 assert_eq!(s.disk_cache_size, 16 * 1024 * 1024);
1468 assert!((s.disk_write_cache_ratio - 0.5).abs() < f32::EPSILON);
1469 assert_eq!(s.disk_channel_capacity, 512);
1470 assert_eq!(s.hashing_threads, default_hashing_threads());
1471 assert_eq!(s.max_request_queue_depth, 250);
1472 assert_eq!(s.initial_queue_depth, 128);
1473 assert!((s.request_queue_time - 3.0).abs() < f64::EPSILON);
1474 assert_eq!(s.block_request_timeout_secs, 60);
1475 assert_eq!(s.max_concurrent_stream_reads, 8);
1476 assert!(!s.force_proxy);
1477 assert!(s.apply_ip_filter_to_trackers);
1478 assert_eq!(s.dht_queries_per_second, 50);
1479 assert_eq!(s.dht_query_timeout_secs, 5);
1480 assert!(!s.dht_enforce_node_id);
1481 assert!(s.dht_restrict_routing_ips);
1482 assert_eq!(s.upnp_lease_duration, 3600);
1483 assert_eq!(s.natpmp_lifetime, 7200);
1484 assert_eq!(s.utp_max_connections, 256);
1485 assert_eq!(s.mixed_mode_algorithm, MixedModeAlgorithm::PeerProportional);
1486 assert!(s.auto_sequential);
1487 assert!(s.strict_end_game);
1488 assert_eq!(s.max_web_seeds, 4);
1489 assert_eq!(s.initial_picker_threshold, 4);
1490 assert_eq!(s.whole_pieces_threshold, 20);
1491 assert_eq!(s.snub_timeout_secs, 15);
1492 assert_eq!(s.readahead_pieces, 8);
1493 assert!(s.streaming_timeout_escalation);
1494 assert_eq!(s.max_peers_per_torrent, 128);
1495 assert_eq!(s.runtime_worker_threads, default_runtime_worker_threads());
1496 assert!(s.pin_cores);
1497 }
1498
1499 #[test]
1500 fn min_memory_preset() {
1501 let s = Settings::min_memory();
1502 assert_eq!(s.disk_cache_size, 8 * 1024 * 1024);
1503 assert_eq!(s.max_torrents, 20);
1504 assert_eq!(s.max_peers_per_torrent, 30);
1505 assert_eq!(s.active_downloads, 1);
1506 assert_eq!(s.active_seeds, 2);
1507 assert_eq!(s.active_limit, 10);
1508 assert_eq!(s.alert_channel_size, 256);
1509 assert_eq!(s.utp_max_connections, 64);
1510 assert_eq!(s.max_request_queue_depth, 50);
1511 assert_eq!(s.initial_queue_depth, 16);
1512 assert_eq!(s.max_concurrent_stream_reads, 2);
1513 assert_eq!(s.hashing_threads, 1);
1514 assert_eq!(s.disk_io_threads, 1);
1515 }
1516
1517 #[test]
1518 fn high_performance_preset() {
1519 let s = Settings::high_performance();
1520 assert_eq!(s.disk_cache_size, 256 * 1024 * 1024);
1521 assert_eq!(s.max_torrents, 2000);
1522 assert_eq!(s.max_peers_per_torrent, 200);
1523 assert_eq!(s.active_downloads, 30);
1524 assert_eq!(s.active_seeds, 100);
1525 assert_eq!(s.active_limit, 2000);
1526 assert_eq!(s.alert_channel_size, 4096);
1527 assert_eq!(s.utp_max_connections, 1024);
1528 assert_eq!(s.max_request_queue_depth, 1000);
1529 assert_eq!(s.initial_queue_depth, 256);
1530 assert_eq!(s.max_concurrent_stream_reads, 32);
1531 assert_eq!(s.hashing_threads, 4);
1532 assert_eq!(s.disk_io_threads, 8);
1533 assert_eq!(s.auto_upload_slots_max, 100);
1534 }
1535
1536 #[test]
1537 fn json_round_trip() {
1538 let original = Settings::default();
1539 let json = serde_json::to_string(&original).unwrap();
1540 let decoded: Settings = serde_json::from_str(&json).unwrap();
1541 assert_eq!(original, decoded);
1542 }
1543
1544 #[test]
1545 fn json_round_trip_presets() {
1546 for original in [Settings::min_memory(), Settings::high_performance()] {
1548 let json = serde_json::to_string(&original).unwrap();
1549 let decoded: Settings = serde_json::from_str(&json).unwrap();
1550 assert_eq!(original, decoded);
1551 }
1552 }
1553
1554 #[test]
1555 fn json_missing_fields_use_defaults() {
1556 let decoded: Settings = serde_json::from_str("{}").unwrap();
1558 assert_eq!(decoded, Settings::default());
1559 }
1560
1561 #[test]
1562 fn validation_force_proxy_no_proxy() {
1563 let mut s = Settings::default();
1564 s.force_proxy = true;
1565 let err = s.validate().unwrap_err();
1567 assert!(err.to_string().contains("force_proxy"));
1568 }
1569
1570 #[test]
1571 fn validation_valid_defaults() {
1572 Settings::default().validate().unwrap();
1573 Settings::min_memory().validate().unwrap();
1574 Settings::high_performance().validate().unwrap();
1575 }
1576
1577 #[test]
1578 fn disk_config_from_settings() {
1579 let s = Settings::default();
1580 let dc = crate::disk::DiskConfig::from(&s);
1581 assert_eq!(dc.io_threads, default_disk_io_threads());
1582 assert_eq!(dc.storage_mode, StorageMode::Auto);
1583 assert_eq!(dc.cache_size, 16 * 1024 * 1024);
1584 assert!((dc.write_cache_ratio - 0.5).abs() < f32::EPSILON);
1585 assert_eq!(dc.channel_capacity, 512);
1586 }
1587
1588 #[test]
1589 fn torrent_config_from_settings() {
1590 let s = Settings::default();
1591 let tc = crate::types::TorrentConfig::from(&s);
1592 assert_eq!(tc.listen_port, 0); assert_eq!(tc.max_peers, s.max_peers_per_torrent);
1594 assert_eq!(tc.download_dir, s.download_dir);
1595 assert_eq!(tc.enable_dht, s.enable_dht);
1596 assert_eq!(tc.enable_pex, s.enable_pex);
1597 assert_eq!(tc.encryption_mode, s.encryption_mode);
1598 assert_eq!(tc.enable_utp, s.enable_utp);
1599 assert_eq!(tc.enable_web_seed, s.enable_web_seed);
1600 assert_eq!(tc.hashing_threads, s.hashing_threads);
1601 assert_eq!(
1602 tc.max_concurrent_stream_reads,
1603 s.max_concurrent_stream_reads
1604 );
1605 assert_eq!(tc.anonymous_mode, s.anonymous_mode);
1606 assert_eq!(tc.enable_i2p, s.enable_i2p);
1607 assert_eq!(tc.allow_i2p_mixed, s.allow_i2p_mixed);
1608 assert_eq!(tc.strict_end_game, s.strict_end_game);
1610 assert_eq!(tc.upload_rate_limit, s.upload_rate_limit);
1611 assert_eq!(tc.download_rate_limit, s.download_rate_limit);
1612 assert_eq!(tc.max_web_seeds, s.max_web_seeds);
1613 assert_eq!(tc.initial_picker_threshold, s.initial_picker_threshold);
1614 assert_eq!(tc.whole_pieces_threshold, s.whole_pieces_threshold);
1615 assert_eq!(tc.snub_timeout_secs, s.snub_timeout_secs);
1616 assert_eq!(tc.readahead_pieces, s.readahead_pieces);
1617 assert_eq!(
1618 tc.streaming_timeout_escalation,
1619 s.streaming_timeout_escalation
1620 );
1621 assert_eq!(tc.storage_mode, s.storage_mode);
1623 assert_eq!(tc.block_request_timeout_secs, s.block_request_timeout_secs);
1624 assert_eq!(tc.enable_lsd, s.enable_lsd);
1625 assert_eq!(tc.force_proxy, s.force_proxy);
1626 assert_eq!(tc.steal_stale_piece_secs, 2);
1628 assert_eq!(tc.steal_stale_piece_secs, s.steal_stale_piece_secs);
1629 }
1630
1631 #[test]
1632 fn torrent_config_from_nondefault_settings() {
1633 let mut s = Settings::default();
1635 s.strict_end_game = false;
1636 s.upload_rate_limit = 1_000_000;
1637 s.download_rate_limit = 2_000_000;
1638 s.max_web_seeds = 8;
1639 s.initial_picker_threshold = 10;
1640 s.whole_pieces_threshold = 50;
1641 s.snub_timeout_secs = 120;
1642 s.readahead_pieces = 16;
1643 s.streaming_timeout_escalation = false;
1644 s.storage_mode = StorageMode::Full;
1645 s.block_request_timeout_secs = 30;
1646 s.enable_lsd = false;
1647 s.force_proxy = true;
1648 s.proxy.proxy_type = crate::proxy::ProxyType::Socks5;
1649
1650 let tc = crate::types::TorrentConfig::from(&s);
1651 assert!(!tc.strict_end_game);
1652 assert_eq!(tc.upload_rate_limit, 1_000_000);
1653 assert_eq!(tc.download_rate_limit, 2_000_000);
1654 assert_eq!(tc.max_web_seeds, 8);
1655 assert_eq!(tc.initial_picker_threshold, 10);
1656 assert_eq!(tc.whole_pieces_threshold, 50);
1657 assert_eq!(tc.snub_timeout_secs, 120);
1658 assert_eq!(tc.readahead_pieces, 16);
1659 assert!(!tc.streaming_timeout_escalation);
1660 assert_eq!(tc.storage_mode, StorageMode::Full);
1661 assert_eq!(tc.block_request_timeout_secs, 30);
1662 assert!(!tc.enable_lsd);
1663 assert!(tc.force_proxy);
1664 }
1665
1666 #[test]
1667 fn external_ip_default_and_json() {
1668 let s = Settings::default();
1669 assert!(s.external_ip.is_none());
1670
1671 let json = r#"{"external_ip": "203.0.113.5"}"#;
1673 let decoded: Settings = serde_json::from_str(json).unwrap();
1674 assert_eq!(
1675 decoded.external_ip,
1676 Some(std::net::IpAddr::V4(std::net::Ipv4Addr::new(
1677 203, 0, 113, 5
1678 )))
1679 );
1680
1681 let encoded = serde_json::to_string(&decoded).unwrap();
1683 let roundtrip: Settings = serde_json::from_str(&encoded).unwrap();
1684 assert_eq!(roundtrip.external_ip, decoded.external_ip);
1685 }
1686
1687 #[test]
1688 fn validation_zero_threads() {
1689 let mut s = Settings::default();
1690 s.hashing_threads = 0;
1691 let err = s.validate().unwrap_err();
1692 assert!(err.to_string().contains("hashing_threads"));
1693
1694 let mut s = Settings::default();
1695 s.disk_io_threads = 0;
1696 let err = s.validate().unwrap_err();
1697 assert!(err.to_string().contains("disk_io_threads"));
1698
1699 let mut s = Settings::default();
1700 s.max_blocking_threads = 0;
1701 let err = s.validate().unwrap_err();
1702 assert!(err.to_string().contains("max_blocking_threads"));
1703 }
1704
1705 #[test]
1706 fn share_mode_requires_fast_extension() {
1707 let mut s = Settings::default();
1708 s.default_share_mode = true;
1709 s.enable_fast_extension = false;
1710 let err = s.validate().unwrap_err();
1711 assert!(err.to_string().contains("share_mode"));
1712
1713 s.enable_fast_extension = true;
1715 s.validate().unwrap();
1716 }
1717
1718 #[test]
1719 fn share_mode_default_false() {
1720 let cfg = crate::types::TorrentConfig::default();
1721 assert!(!cfg.share_mode);
1722 }
1723
1724 #[test]
1725 fn dht_storage_settings_defaults() {
1726 let s = Settings::default();
1727 assert_eq!(s.dht_max_items, 700);
1728 assert_eq!(s.dht_item_lifetime_secs, 7200);
1729 }
1730
1731 #[test]
1732 fn dht_sample_interval_default_disabled() {
1733 let s = Settings::default();
1734 assert_eq!(s.dht_sample_infohashes_interval, 0);
1735 }
1736
1737 #[test]
1738 fn dht_sample_interval_json_round_trip() {
1739 let json = r#"{"dht_sample_infohashes_interval": 300}"#;
1740 let decoded: Settings = serde_json::from_str(json).unwrap();
1741 assert_eq!(decoded.dht_sample_infohashes_interval, 300);
1742
1743 let encoded = serde_json::to_string(&decoded).unwrap();
1744 let roundtrip: Settings = serde_json::from_str(&encoded).unwrap();
1745 assert_eq!(roundtrip.dht_sample_infohashes_interval, 300);
1746 }
1747
1748 #[test]
1749 fn min_memory_restricts_dht_items() {
1750 let s = Settings::min_memory();
1751 assert_eq!(s.dht_max_items, 100);
1752 }
1753
1754 #[test]
1755 fn dht_config_inherits_security_settings() {
1756 let mut s = Settings::default();
1757 s.dht_enforce_node_id = false;
1758 let dht = s.to_dht_config();
1759 assert!(!dht.enforce_node_id);
1760 assert!(dht.restrict_routing_ips);
1761
1762 let dht_v6 = s.to_dht_config_v6();
1763 assert!(!dht_v6.enforce_node_id);
1764 assert!(dht_v6.restrict_routing_ips);
1765 }
1766
1767 #[test]
1768 fn enable_holepunch_default_true() {
1769 let s = Settings::default();
1770 assert!(s.enable_holepunch);
1771 }
1772
1773 #[test]
1774 fn enable_holepunch_json_round_trip() {
1775 let json = r#"{"enable_holepunch": false}"#;
1776 let decoded: Settings = serde_json::from_str(json).unwrap();
1777 assert!(!decoded.enable_holepunch);
1778
1779 let encoded = serde_json::to_string(&decoded).unwrap();
1780 let roundtrip: Settings = serde_json::from_str(&encoded).unwrap();
1781 assert!(!roundtrip.enable_holepunch);
1782 }
1783
1784 #[test]
1785 fn i2p_settings_defaults() {
1786 let s = Settings::default();
1787 assert!(!s.enable_i2p);
1788 assert_eq!(s.i2p_hostname, "127.0.0.1");
1789 assert_eq!(s.i2p_port, 7656);
1790 assert_eq!(s.i2p_inbound_quantity, 3);
1791 assert_eq!(s.i2p_outbound_quantity, 3);
1792 assert_eq!(s.i2p_inbound_length, 3);
1793 assert_eq!(s.i2p_outbound_length, 3);
1794 assert!(!s.allow_i2p_mixed);
1795 }
1796
1797 #[test]
1798 fn i2p_settings_json_roundtrip() {
1799 let mut s = Settings::default();
1800 s.enable_i2p = true;
1801 s.i2p_hostname = "10.0.0.1".into();
1802 s.i2p_port = 7700;
1803 s.i2p_inbound_quantity = 5;
1804 s.i2p_outbound_quantity = 4;
1805 s.i2p_inbound_length = 2;
1806 s.i2p_outbound_length = 1;
1807 s.allow_i2p_mixed = true;
1808 let json = serde_json::to_string(&s).unwrap();
1809 let decoded: Settings = serde_json::from_str(&json).unwrap();
1810 assert_eq!(s, decoded);
1811 }
1812
1813 #[test]
1814 fn i2p_validation_quantity_zero() {
1815 let mut s = Settings::default();
1816 s.enable_i2p = true;
1817 s.i2p_inbound_quantity = 0;
1818 let err = s.validate().unwrap_err();
1819 assert!(err.to_string().contains("i2p_inbound_quantity"));
1820 }
1821
1822 #[test]
1823 fn i2p_validation_quantity_too_high() {
1824 let mut s = Settings::default();
1825 s.enable_i2p = true;
1826 s.i2p_outbound_quantity = 17;
1827 let err = s.validate().unwrap_err();
1828 assert!(err.to_string().contains("i2p_outbound_quantity"));
1829 }
1830
1831 #[test]
1832 fn i2p_validation_length_too_high() {
1833 let mut s = Settings::default();
1834 s.enable_i2p = true;
1835 s.i2p_inbound_length = 8;
1836 let err = s.validate().unwrap_err();
1837 assert!(err.to_string().contains("i2p_inbound_length"));
1838 }
1839
1840 #[test]
1841 fn i2p_validation_passes_when_disabled() {
1842 let mut s = Settings::default();
1844 s.enable_i2p = false;
1845 s.i2p_inbound_quantity = 0; s.validate().unwrap(); }
1848
1849 #[test]
1850 fn i2p_validation_valid_config() {
1851 let mut s = Settings::default();
1852 s.enable_i2p = true;
1853 s.i2p_inbound_quantity = 1;
1854 s.i2p_outbound_quantity = 16;
1855 s.i2p_inbound_length = 0;
1856 s.i2p_outbound_length = 7;
1857 s.validate().unwrap();
1858 }
1859
1860 #[test]
1861 fn ssl_settings_defaults() {
1862 let s = Settings::default();
1863 assert_eq!(s.ssl_listen_port, 0);
1864 assert!(s.ssl_cert_path.is_none());
1865 assert!(s.ssl_key_path.is_none());
1866 }
1867
1868 #[test]
1869 fn ssl_settings_json_round_trip() {
1870 let mut s = Settings::default();
1871 s.ssl_listen_port = 4433;
1872 s.ssl_cert_path = Some(PathBuf::from("/etc/ssl/cert.pem"));
1873 s.ssl_key_path = Some(PathBuf::from("/etc/ssl/key.pem"));
1874 let json = serde_json::to_string(&s).unwrap();
1875 let decoded: Settings = serde_json::from_str(&json).unwrap();
1876 assert_eq!(s, decoded);
1877 }
1878
1879 #[test]
1880 fn ssl_validation_cert_without_key() {
1881 let mut s = Settings::default();
1882 s.ssl_cert_path = Some(PathBuf::from("/tmp/cert.pem"));
1883 let err = s.validate().unwrap_err();
1885 assert!(err.to_string().contains("ssl_cert_path"));
1886 }
1887
1888 #[test]
1889 fn ssl_validation_key_without_cert() {
1890 let mut s = Settings::default();
1891 s.ssl_key_path = Some(PathBuf::from("/tmp/key.pem"));
1892 let err = s.validate().unwrap_err();
1894 assert!(err.to_string().contains("ssl_cert_path"));
1895 }
1896
1897 #[test]
1898 fn ssl_validation_both_set_passes() {
1899 let mut s = Settings::default();
1900 s.ssl_cert_path = Some(PathBuf::from("/tmp/cert.pem"));
1901 s.ssl_key_path = Some(PathBuf::from("/tmp/key.pem"));
1902 s.validate().unwrap();
1903 }
1904
1905 #[test]
1906 fn ssl_validation_both_absent_passes() {
1907 let s = Settings::default();
1908 s.validate().unwrap();
1910 }
1911
1912 #[test]
1913 fn default_choking_algorithms() {
1914 let s = Settings::default();
1915 assert_eq!(
1916 s.seed_choking_algorithm,
1917 SeedChokingAlgorithm::FastestUpload
1918 );
1919 assert_eq!(s.choking_algorithm, ChokingAlgorithm::FixedSlots);
1920 }
1921
1922 #[test]
1923 fn choking_algorithm_json_round_trip() {
1924 let mut s = Settings::default();
1925 s.seed_choking_algorithm = SeedChokingAlgorithm::AntiLeech;
1926 s.choking_algorithm = ChokingAlgorithm::RateBased;
1927 let json = serde_json::to_string(&s).unwrap();
1928 let decoded: Settings = serde_json::from_str(&json).unwrap();
1929 assert_eq!(
1930 decoded.seed_choking_algorithm,
1931 SeedChokingAlgorithm::AntiLeech
1932 );
1933 assert_eq!(decoded.choking_algorithm, ChokingAlgorithm::RateBased);
1934 }
1935
1936 #[test]
1937 fn m44_settings_defaults() {
1938 let s = Settings::default();
1939 assert!(s.piece_extent_affinity);
1940 assert!(!s.suggest_mode);
1941 assert_eq!(s.max_suggest_pieces, 16);
1942 assert_eq!(s.predictive_piece_announce_ms, 0);
1943 }
1944
1945 #[test]
1946 fn m44_high_performance_enables_suggest() {
1947 let s = Settings::high_performance();
1948 assert!(s.suggest_mode);
1949 }
1950
1951 #[test]
1952 fn m44_json_round_trip() {
1953 let mut s = Settings::default();
1954 s.piece_extent_affinity = false;
1955 s.suggest_mode = true;
1956 s.max_suggest_pieces = 5;
1957 s.predictive_piece_announce_ms = 50;
1958 let json = serde_json::to_string(&s).unwrap();
1959 let decoded: Settings = serde_json::from_str(&json).unwrap();
1960 assert_eq!(s, decoded);
1961 }
1962
1963 #[test]
1964 fn security_settings_defaults() {
1965 let s = Settings::default();
1966 assert!(s.ssrf_mitigation);
1967 assert!(!s.allow_idna);
1968 assert!(s.validate_https_trackers);
1969 }
1970
1971 #[test]
1972 fn security_settings_json_round_trip() {
1973 let mut s = Settings::default();
1974 s.ssrf_mitigation = false;
1975 s.allow_idna = true;
1976 s.validate_https_trackers = false;
1977 let json = serde_json::to_string(&s).unwrap();
1978 let decoded: Settings = serde_json::from_str(&json).unwrap();
1979 assert_eq!(s, decoded);
1980 }
1981
1982 #[test]
1983 fn security_settings_missing_use_defaults() {
1984 let decoded: Settings = serde_json::from_str("{}").unwrap();
1986 assert!(decoded.ssrf_mitigation);
1987 assert!(!decoded.allow_idna);
1988 assert!(decoded.validate_https_trackers);
1989 }
1990
1991 #[test]
1992 fn url_security_config_from_settings() {
1993 let mut s = Settings::default();
1994 s.ssrf_mitigation = false;
1995 s.allow_idna = true;
1996 s.validate_https_trackers = false;
1997 let cfg = crate::url_guard::UrlSecurityConfig::from(&s);
1998 assert!(!cfg.ssrf_mitigation);
1999 assert!(cfg.allow_idna);
2000 assert!(!cfg.validate_https_trackers);
2001 }
2002
2003 #[test]
2004 fn default_peer_dscp_value() {
2005 let s = Settings::default();
2006 assert_eq!(s.peer_dscp, 0x08);
2007 }
2008
2009 #[test]
2010 fn peer_dscp_json_round_trip() {
2011 let mut s = Settings::default();
2012 s.peer_dscp = 0x2E; let json = serde_json::to_string(&s).unwrap();
2014 let decoded: Settings = serde_json::from_str(&json).unwrap();
2015 assert_eq!(decoded.peer_dscp, 0x2E);
2016 }
2017
2018 #[test]
2019 fn peer_dscp_zero_disables() {
2020 let mut s = Settings::default();
2021 s.peer_dscp = 0;
2022 let json = serde_json::to_string(&s).unwrap();
2023 let decoded: Settings = serde_json::from_str(&json).unwrap();
2024 assert_eq!(decoded.peer_dscp, 0);
2025 }
2026
2027 #[test]
2028 fn utp_config_includes_dscp() {
2029 let mut s = Settings::default();
2030 s.peer_dscp = 0x0A;
2031 let utp = s.to_utp_config(6881);
2032 assert_eq!(utp.dscp, 0x0A);
2033
2034 let utp_v6 = s.to_utp_config_v6(6881);
2035 assert_eq!(utp_v6.dscp, 0x0A);
2036 }
2037
2038 #[test]
2039 fn default_stats_report_interval() {
2040 let s = Settings::default();
2041 assert_eq!(s.stats_report_interval, 1000);
2042 }
2043
2044 #[test]
2045 fn stats_report_interval_json_round_trip() {
2046 let mut s = Settings::default();
2047 s.stats_report_interval = 5000;
2048 let json = serde_json::to_string(&s).unwrap();
2049 let decoded: Settings = serde_json::from_str(&json).unwrap();
2050 assert_eq!(decoded.stats_report_interval, 5000);
2051 }
2052
2053 #[test]
2054 fn stats_report_interval_zero_disables() {
2055 let mut s = Settings::default();
2056 s.stats_report_interval = 0;
2057 let json = serde_json::to_string(&s).unwrap();
2058 let decoded: Settings = serde_json::from_str(&json).unwrap();
2059 assert_eq!(decoded.stats_report_interval, 0);
2060 }
2061
2062 #[test]
2063 fn settings_runtime_worker_threads_and_pin_cores() {
2064 let s = Settings::default();
2066 assert_eq!(s.runtime_worker_threads, default_runtime_worker_threads());
2067 assert!(s.pin_cores);
2068
2069 let mut s = Settings::default();
2071 s.runtime_worker_threads = 0;
2072 assert!(s.validate().is_ok());
2073
2074 s.runtime_worker_threads = 256;
2076 assert!(s.validate().is_ok());
2077
2078 s.runtime_worker_threads = 257;
2080 assert!(s.validate().is_err());
2081 }
2082
2083 #[test]
2084 fn max_in_flight_512_default() {
2085 let s = Settings::default();
2086 assert_eq!(s.max_in_flight_pieces, 512);
2087 assert_eq!(s.fixed_pipeline_depth, 128);
2088
2089 let mm = Settings::min_memory();
2091 assert_eq!(mm.max_in_flight_pieces, 32);
2092 assert_eq!(mm.fixed_pipeline_depth, 32);
2093
2094 let hp = Settings::high_performance();
2095 assert_eq!(hp.max_in_flight_pieces, 512);
2096 assert_eq!(hp.fixed_pipeline_depth, 128); }
2098
2099 #[test]
2100 fn recalc_max_in_flight_formula() {
2101 let base = 512_usize;
2104
2105 let connected = 10;
2107 let num_pieces = 2000_u32;
2108 let calculated = base.max(connected * 4);
2109 let result = calculated.min(num_pieces as usize / 2).max(base);
2110 assert_eq!(result, 512); let connected = 200;
2114 let calculated = base.max(connected * 4);
2115 let result = calculated.min(num_pieces as usize / 2).max(base);
2116 assert_eq!(result, 800); let connected = 200;
2120 let num_pieces = 100_u32;
2121 let calculated = base.max(connected * 4);
2122 let result = calculated.min(num_pieces as usize / 2).max(base);
2123 assert_eq!(result, 512); let connected = 129; let num_pieces = 10000_u32;
2128 let calculated = base.max(connected * 4);
2129 let result = calculated.min(num_pieces as usize / 2).max(base);
2130 assert_eq!(result, 516); }
2132}