11use std:: error:: Error ;
22use std:: fmt;
3- use std:: net:: SocketAddr ;
43
54use reqwest:: Url as ServiceUrl ;
65use serde:: Deserialize ;
@@ -31,7 +30,7 @@ struct PlainConfiguration {
3130
3231/// Validated configuration
3332pub struct Configuration {
34- pub udp_trackers : Vec < SocketAddr > ,
33+ pub udp_trackers : Vec < ServiceUrl > ,
3534 pub http_trackers : Vec < ServiceUrl > ,
3635 pub health_checks : Vec < ServiceUrl > ,
3736}
@@ -62,7 +61,8 @@ impl TryFrom<PlainConfiguration> for Configuration {
6261 let udp_trackers = plain_config
6362 . udp_trackers
6463 . into_iter ( )
65- . map ( |s| s. parse :: < SocketAddr > ( ) . map_err ( ConfigurationError :: InvalidUdpAddress ) )
64+ . map ( |s| if s. starts_with ( "udp://" ) { s } else { format ! ( "udp://{s}" ) } )
65+ . map ( |s| s. parse :: < ServiceUrl > ( ) . map_err ( ConfigurationError :: InvalidUrl ) )
6666 . collect :: < Result < Vec < _ > , _ > > ( ) ?;
6767
6868 let http_trackers = plain_config
@@ -87,68 +87,161 @@ impl TryFrom<PlainConfiguration> for Configuration {
8787
8888#[ cfg( test) ]
8989mod tests {
90- use std:: net:: { IpAddr , Ipv4Addr } ;
91-
9290 use super :: * ;
9391
9492 #[ test]
9593 fn configuration_should_be_build_from_plain_serializable_configuration ( ) {
9694 let dto = PlainConfiguration {
97- udp_trackers : vec ! [ "127.0.0.1:8080" . to_string( ) ] ,
95+ udp_trackers : vec ! [ "udp:// 127.0.0.1:8080" . to_string( ) ] ,
9896 http_trackers : vec ! [ "http://127.0.0.1:8080" . to_string( ) ] ,
9997 health_checks : vec ! [ "http://127.0.0.1:8080/health" . to_string( ) ] ,
10098 } ;
10199
102100 let config = Configuration :: try_from ( dto) . expect ( "A valid configuration" ) ;
103101
104- assert_eq ! (
105- config. udp_trackers,
106- vec![ SocketAddr :: new( IpAddr :: V4 ( Ipv4Addr :: new( 127 , 0 , 0 , 1 ) ) , 8080 ) ]
107- ) ;
102+ assert_eq ! ( config. udp_trackers, vec![ ServiceUrl :: parse( "udp://127.0.0.1:8080" ) . unwrap( ) ] ) ;
103+
108104 assert_eq ! (
109105 config. http_trackers,
110106 vec![ ServiceUrl :: parse( "http://127.0.0.1:8080" ) . unwrap( ) ]
111107 ) ;
108+
112109 assert_eq ! (
113110 config. health_checks,
114111 vec![ ServiceUrl :: parse( "http://127.0.0.1:8080/health" ) . unwrap( ) ]
115112 ) ;
116113 }
117114
118- mod building_configuration_from_plan_configuration {
119- use crate :: console:: clients:: checker:: config:: { Configuration , PlainConfiguration } ;
115+ mod building_configuration_from_plain_configuration_for {
116+
117+ mod udp_trackers {
118+ use crate :: console:: clients:: checker:: config:: { Configuration , PlainConfiguration , ServiceUrl } ;
119+
120+ /* The plain configuration should allow UDP URLs with:
121+
122+ - IP or domain.
123+ - With or without scheme.
124+ - With or without `announce` suffix.
125+ - With or without `/` at the end of the authority section (with empty path).
126+
127+ For example:
128+
129+ 127.0.0.1:6969
130+ 127.0.0.1:6969/
131+ 127.0.0.1:6969/announce
132+
133+ localhost:6969
134+ localhost:6969/
135+ localhost:6969/announce
136+
137+ udp://127.0.0.1:6969
138+ udp://127.0.0.1:6969/
139+ udp://127.0.0.1:6969/announce
140+
141+ udp://localhost:6969
142+ udp://localhost:6969/
143+ udp://localhost:6969/announce
144+
145+ */
120146
121- #[ test]
122- fn it_should_fail_when_a_tracker_udp_address_is_invalid ( ) {
123- let plain_config = PlainConfiguration {
124- udp_trackers : vec ! [ "invalid_address " . to_string( ) ] ,
125- http_trackers : vec ! [ ] ,
126- health_checks : vec ! [ ] ,
127- } ;
147+ #[ test]
148+ fn it_should_fail_when_a_tracker_udp_url_is_invalid ( ) {
149+ let plain_config = PlainConfiguration {
150+ udp_trackers : vec ! [ "invalid URL " . to_string( ) ] ,
151+ http_trackers : vec ! [ ] ,
152+ health_checks : vec ! [ ] ,
153+ } ;
128154
129- assert ! ( Configuration :: try_from( plain_config) . is_err( ) ) ;
155+ assert ! ( Configuration :: try_from( plain_config) . is_err( ) ) ;
156+ }
157+
158+ #[ test]
159+ fn it_should_add_the_udp_scheme_to_the_udp_url_when_it_is_missing ( ) {
160+ let plain_config = PlainConfiguration {
161+ udp_trackers : vec ! [ "127.0.0.1:6969" . to_string( ) ] ,
162+ http_trackers : vec ! [ ] ,
163+ health_checks : vec ! [ ] ,
164+ } ;
165+
166+ let config = Configuration :: try_from ( plain_config) . expect ( "Invalid plain configuration" ) ;
167+
168+ assert_eq ! ( config. udp_trackers[ 0 ] , "udp://127.0.0.1:6969" . parse:: <ServiceUrl >( ) . unwrap( ) ) ;
169+ }
170+
171+ #[ test]
172+ fn it_should_allow_using_domains ( ) {
173+ let plain_config = PlainConfiguration {
174+ udp_trackers : vec ! [ "udp://localhost:6969" . to_string( ) ] ,
175+ http_trackers : vec ! [ ] ,
176+ health_checks : vec ! [ ] ,
177+ } ;
178+
179+ let config = Configuration :: try_from ( plain_config) . expect ( "Invalid plain configuration" ) ;
180+
181+ assert_eq ! ( config. udp_trackers[ 0 ] , "udp://localhost:6969" . parse:: <ServiceUrl >( ) . unwrap( ) ) ;
182+ }
183+
184+ #[ test]
185+ fn it_should_allow_the_url_to_have_an_empty_path ( ) {
186+ let plain_config = PlainConfiguration {
187+ udp_trackers : vec ! [ "127.0.0.1:6969/" . to_string( ) ] ,
188+ http_trackers : vec ! [ ] ,
189+ health_checks : vec ! [ ] ,
190+ } ;
191+
192+ let config = Configuration :: try_from ( plain_config) . expect ( "Invalid plain configuration" ) ;
193+
194+ assert_eq ! ( config. udp_trackers[ 0 ] , "udp://127.0.0.1:6969/" . parse:: <ServiceUrl >( ) . unwrap( ) ) ;
195+ }
196+
197+ #[ test]
198+ fn it_should_allow_the_url_to_contain_a_path ( ) {
199+ // This is the common format for UDP tracker URLs:
200+ // udp://domain.com:6969/announce
201+
202+ let plain_config = PlainConfiguration {
203+ udp_trackers : vec ! [ "127.0.0.1:6969/announce" . to_string( ) ] ,
204+ http_trackers : vec ! [ ] ,
205+ health_checks : vec ! [ ] ,
206+ } ;
207+
208+ let config = Configuration :: try_from ( plain_config) . expect ( "Invalid plain configuration" ) ;
209+
210+ assert_eq ! (
211+ config. udp_trackers[ 0 ] ,
212+ "udp://127.0.0.1:6969/announce" . parse:: <ServiceUrl >( ) . unwrap( )
213+ ) ;
214+ }
130215 }
131216
132- #[ test]
133- fn it_should_fail_when_a_tracker_http_address_is_invalid ( ) {
134- let plain_config = PlainConfiguration {
135- udp_trackers : vec ! [ ] ,
136- http_trackers : vec ! [ "not_a_url" . to_string( ) ] ,
137- health_checks : vec ! [ ] ,
138- } ;
217+ mod http_trackers {
218+ use crate :: console:: clients:: checker:: config:: { Configuration , PlainConfiguration } ;
219+
220+ #[ test]
221+ fn it_should_fail_when_a_tracker_http_url_is_invalid ( ) {
222+ let plain_config = PlainConfiguration {
223+ udp_trackers : vec ! [ ] ,
224+ http_trackers : vec ! [ "invalid URL" . to_string( ) ] ,
225+ health_checks : vec ! [ ] ,
226+ } ;
139227
140- assert ! ( Configuration :: try_from( plain_config) . is_err( ) ) ;
228+ assert ! ( Configuration :: try_from( plain_config) . is_err( ) ) ;
229+ }
141230 }
142231
143- #[ test]
144- fn it_should_fail_when_a_health_check_http_address_is_invalid ( ) {
145- let plain_config = PlainConfiguration {
146- udp_trackers : vec ! [ ] ,
147- http_trackers : vec ! [ ] ,
148- health_checks : vec ! [ "not_a_url" . to_string( ) ] ,
149- } ;
232+ mod health_checks {
233+ use crate :: console:: clients:: checker:: config:: { Configuration , PlainConfiguration } ;
234+
235+ #[ test]
236+ fn it_should_fail_when_a_health_check_http_url_is_invalid ( ) {
237+ let plain_config = PlainConfiguration {
238+ udp_trackers : vec ! [ ] ,
239+ http_trackers : vec ! [ ] ,
240+ health_checks : vec ! [ "invalid URL" . to_string( ) ] ,
241+ } ;
150242
151- assert ! ( Configuration :: try_from( plain_config) . is_err( ) ) ;
243+ assert ! ( Configuration :: try_from( plain_config) . is_err( ) ) ;
244+ }
152245 }
153246 }
154247}
0 commit comments