@@ -210,10 +210,11 @@ public void Add_AlreadyStarted_ReturnsExpected()
210210 }
211211 }
212212
213- [ Fact ]
214- public void Add_PrefixAlreadyRegisteredAndNotStarted_ThrowsHttpListenerException ( )
213+ [ ConditionalTheory ( nameof ( PlatformDetection ) + "." + nameof ( PlatformDetection . IsNotOneCoreUAP ) ) ]
214+ [ MemberData ( nameof ( Hosts_TestData ) ) ]
215+ public void Add_PrefixAlreadyRegisteredAndNotStarted_ThrowsHttpListenerException ( string hostname )
215216 {
216- using ( var factory = new HttpListenerFactory ( ) )
217+ using ( var factory = new HttpListenerFactory ( hostname ) )
217218 {
218219 string uriPrefix = Assert . Single ( factory . GetListener ( ) . Prefixes ) ;
219220
@@ -224,10 +225,28 @@ public void Add_PrefixAlreadyRegisteredAndNotStarted_ThrowsHttpListenerException
224225 }
225226 }
226227
227- [ Fact ]
228- public void Add_PrefixAlreadyRegisteredAndStarted_ThrowsHttpListenerException ( )
228+ [ ConditionalTheory ( nameof ( PlatformDetection ) + "." + nameof ( PlatformDetection . IsNotOneCoreUAP ) ) ]
229+ [ MemberData ( nameof ( Hosts_TestData ) ) ]
230+ public void Add_PrefixAlreadyRegisteredWithDifferentPathAndNotStarted_Works ( string hostname )
229231 {
230- using ( var factory = new HttpListenerFactory ( ) )
232+ using ( var factory = new HttpListenerFactory ( hostname ) )
233+ {
234+ var listener = factory . GetListener ( ) ;
235+ string uriPrefix = Assert . Single ( listener . Prefixes ) ;
236+
237+ listener . Prefixes . Add ( uriPrefix + "sub_path/" ) ;
238+ Assert . Equal ( 2 , listener . Prefixes . Count ) ;
239+
240+ listener . Start ( ) ;
241+ Assert . True ( listener . IsListening ) ;
242+ }
243+ }
244+
245+ [ ConditionalTheory ( nameof ( PlatformDetection ) + "." + nameof ( PlatformDetection . IsNotOneCoreUAP ) ) ]
246+ [ MemberData ( nameof ( Hosts_TestData ) ) ]
247+ public void Add_PrefixAlreadyRegisteredAndStarted_ThrowsHttpListenerException ( string hostname )
248+ {
249+ using ( var factory = new HttpListenerFactory ( hostname ) )
231250 {
232251 HttpListener listener = factory . GetListener ( ) ;
233252 string uriPrefix = Assert . Single ( listener . Prefixes ) ;
@@ -237,6 +256,227 @@ public void Add_PrefixAlreadyRegisteredAndStarted_ThrowsHttpListenerException()
237256 }
238257 }
239258
259+ public static IEnumerable < object [ ] > Hosts_TestData ( )
260+ {
261+ yield return new object [ ] { "localhost" } ;
262+ yield return new object [ ] { "127.0.0.1" } ;
263+
264+ if ( HttpListenerFactory . SupportsWildcards )
265+ {
266+ yield return new object [ ] { "*" } ;
267+ yield return new object [ ] { "+" } ;
268+ }
269+ }
270+
271+ [ ConditionalTheory ( nameof ( PlatformDetection ) + "." + nameof ( PlatformDetection . IsNotOneCoreUAP ) ) ]
272+ [ MemberData ( nameof ( Hosts_TestData ) ) ]
273+ public void Add_SamePortDifferentPathDifferentListenerNotStarted_Works ( string host )
274+ {
275+ var listener1 = new HttpListener ( ) ;
276+ var listener2 = new HttpListener ( ) ;
277+
278+ int ? freePort = null ;
279+
280+ // Try to find a port that is not being used.
281+ for ( int port = 1025 ; port <= IPEndPoint . MaxPort ; port ++ )
282+ {
283+ string uriPrefix = $ "http://{ host } :{ port } /";
284+ try
285+ {
286+ listener1 . Prefixes . Add ( uriPrefix ) ;
287+ Assert . Equal ( 1 , listener1 . Prefixes . Count ) ;
288+ listener1 . Start ( ) ;
289+
290+ freePort = port ;
291+ break ;
292+ }
293+ catch ( HttpListenerException )
294+ {
295+ // This port is already in use. Skip it and find a port that's not being used.
296+ listener1 . Close ( ) ;
297+ listener1 = new HttpListener ( ) ;
298+ }
299+ }
300+
301+ try
302+ {
303+ if ( ! freePort . HasValue )
304+ {
305+ throw new InvalidOperationException ( "Expected to have a port open on the machine." ) ;
306+ }
307+
308+ listener2 . Prefixes . Add ( $ "http://{ host } :{ freePort } /sub_path/") ;
309+ Assert . Equal ( 1 , listener2 . Prefixes . Count ) ;
310+
311+ listener2 . Start ( ) ;
312+ Assert . True ( listener2 . IsListening ) ;
313+ }
314+ finally
315+ {
316+ listener1 ? . Close ( ) ;
317+ listener2 ? . Close ( ) ;
318+ }
319+ }
320+
321+ [ ConditionalTheory ( nameof ( PlatformDetection ) + "." + nameof ( PlatformDetection . IsNotOneCoreUAP ) ) ]
322+ [ MemberData ( nameof ( Hosts_TestData ) ) ]
323+ public void Add_SamePortDifferentPathDifferentListenerStarted_Works ( string host )
324+ {
325+ var listener1 = new HttpListener ( ) ;
326+ var listener2 = new HttpListener ( ) ;
327+
328+ int ? freePort1 = null ;
329+ int ? freePort2 = null ;
330+
331+ // Try to find a port that is not being used.
332+ for ( int port = 1025 ; port <= IPEndPoint . MaxPort ; port ++ )
333+ {
334+ string uriPrefix = $ "http://127.0.0.1:{ port } /";
335+ try
336+ {
337+ if ( ! freePort1 . HasValue )
338+ {
339+ listener1 . Prefixes . Add ( uriPrefix ) ;
340+ Assert . Equal ( 1 , listener1 . Prefixes . Count ) ;
341+
342+ listener1 . Start ( ) ;
343+ freePort1 = port ;
344+ }
345+ else if ( ! freePort2 . HasValue )
346+ {
347+ listener2 . Prefixes . Add ( uriPrefix ) ;
348+ Assert . Equal ( 1 , listener2 . Prefixes . Count ) ;
349+
350+ listener2 . Start ( ) ;
351+ freePort2 = port ;
352+
353+ break ;
354+ }
355+ }
356+ catch ( HttpListenerException )
357+ {
358+ // This port is already in use. Skip it and find a port that's not being used.
359+ if ( ! freePort1 . HasValue )
360+ {
361+ listener1 . Close ( ) ;
362+ listener1 = new HttpListener ( ) ;
363+ }
364+ else if ( ! freePort2 . HasValue )
365+ {
366+ listener2 . Close ( ) ;
367+ listener2 = new HttpListener ( ) ;
368+ }
369+ }
370+ }
371+
372+ try
373+ {
374+ if ( ! freePort1 . HasValue || ! freePort2 . HasValue )
375+ {
376+ throw new InvalidOperationException ( "Expected to have a port open on the machine." ) ;
377+ }
378+
379+ listener1 . Prefixes . Add ( $ "http://127.0.0.1:{ freePort2 } /hola/") ;
380+ listener2 . Prefixes . Add ( $ "http://127.0.0.1:{ freePort1 } /hola/") ;
381+
382+ Assert . Equal ( 2 , listener1 . Prefixes . Count ) ;
383+ Assert . Equal ( 2 , listener2 . Prefixes . Count ) ;
384+
385+ // Conflicts with existing registration: listener2 has registered to listen to http://127.0.0.1:{freePort1}/...
386+ Assert . Throws < HttpListenerException > ( ( ) => listener1 . Prefixes . Add ( $ "http://127.0.0.1:{ freePort1 } /") ) ;
387+ Assert . Throws < HttpListenerException > ( ( ) => listener1 . Prefixes . Add ( $ "http://127.0.0.1:{ freePort1 } /hola/") ) ;
388+
389+ // Conflicts with existing registration: listener1 has registered to listen to http://127.0.0.1:{freePort2}/...
390+ Assert . Throws < HttpListenerException > ( ( ) => listener2 . Prefixes . Add ( $ "http://127.0.0.1:{ freePort2 } /") ) ;
391+ Assert . Throws < HttpListenerException > ( ( ) => listener2 . Prefixes . Add ( $ "http://127.0.0.1:{ freePort2 } /hola/") ) ;
392+ }
393+ finally
394+ {
395+ listener1 ? . Close ( ) ;
396+ listener2 ? . Close ( ) ;
397+ }
398+ }
399+
400+ [ ConditionalTheory ( nameof ( PlatformDetection ) + "." + nameof ( PlatformDetection . IsNotOneCoreUAP ) ) ]
401+ [ MemberData ( nameof ( Hosts_TestData ) ) ]
402+ public void Add_SamePortDifferentPathMultipleStarted_Success ( string host )
403+ {
404+ var listener1 = new HttpListener ( ) ;
405+ var listener2 = new HttpListener ( ) ;
406+
407+ int ? freePort1 = null ;
408+ int ? freePort2 = null ;
409+
410+ // Try to find a port that is not being used.
411+ for ( int port = 1025 ; port <= IPEndPoint . MaxPort ; port ++ )
412+ {
413+ string uriPrefix = $ "http://{ host } :{ port } /";
414+ try
415+ {
416+ if ( ! freePort1 . HasValue )
417+ {
418+ listener1 . Prefixes . Add ( uriPrefix ) ;
419+ Assert . Equal ( 1 , listener1 . Prefixes . Count ) ;
420+
421+ listener1 . Start ( ) ;
422+ freePort1 = port ;
423+ }
424+ else if ( ! freePort2 . HasValue )
425+ {
426+ listener2 . Prefixes . Add ( uriPrefix ) ;
427+ Assert . Equal ( 1 , listener2 . Prefixes . Count ) ;
428+
429+ listener2 . Start ( ) ;
430+ freePort2 = port ;
431+
432+ break ;
433+ }
434+ }
435+ catch ( HttpListenerException )
436+ {
437+ // This port is already in use. Skip it and find a port that's not being used.
438+ if ( ! freePort1 . HasValue )
439+ {
440+ listener1 . Close ( ) ;
441+ listener1 = new HttpListener ( ) ;
442+ }
443+ else if ( ! freePort2 . HasValue )
444+ {
445+ listener2 . Close ( ) ;
446+ listener2 = new HttpListener ( ) ;
447+ }
448+ }
449+ }
450+
451+ try
452+ {
453+ if ( ! freePort1 . HasValue || ! freePort2 . HasValue )
454+ {
455+ throw new InvalidOperationException ( "Expected to have a port open on the machine." ) ;
456+ }
457+
458+ listener1 . Prefixes . Add ( $ "http://{ host } :{ freePort1 } /hola/") ;
459+ Assert . Equal ( 2 , listener1 . Prefixes . Count ) ;
460+
461+ listener2 . Prefixes . Add ( $ "http://{ host } :{ freePort2 } /hola/") ;
462+ Assert . Equal ( 2 , listener2 . Prefixes . Count ) ;
463+
464+ // Conflict: listenerX is already listening to $"http://127.0.0.1:{freePortX}/hola/".
465+ Assert . Throws < HttpListenerException > ( ( ) => listener1 . Prefixes . Add ( $ "http://{ host } :{ freePort1 } /hola/") ) ;
466+ Assert . Throws < HttpListenerException > ( ( ) => listener2 . Prefixes . Add ( $ "http://{ host } :{ freePort2 } /hola/") ) ;
467+
468+ // Conflict: listenerX is already listening to $"http://127.0.0.1:{freePortY}/hola/".
469+ Assert . Throws < HttpListenerException > ( ( ) => listener1 . Prefixes . Add ( $ "http://{ host } :{ freePort2 } /hola/") ) ;
470+ Assert . Throws < HttpListenerException > ( ( ) => listener2 . Prefixes . Add ( $ "http://{ host } :{ freePort1 } /hola/") ) ;
471+
472+ }
473+ finally
474+ {
475+ listener1 ? . Close ( ) ;
476+ listener2 ? . Close ( ) ;
477+ }
478+ }
479+
240480 public static IEnumerable < object [ ] > InvalidPrefix_TestData ( )
241481 {
242482 // [ActiveIssue(19593, TestPlatforms.OSX)]
0 commit comments