I'm using OWIN to Self-Host Web API while running my tests in parallel using NCrunch and I'm starting it in BeforeEach and stoping in AfterEach methods.
Before each test I'm trying to get available free port, but usually 5-10 tests out of 85 fails with the following exception:
System.Net.HttpListenerException : Failed to listen on prefix
'http://localhost:3369/' because it conflicts with an existing registration on the machine.
So it appears, that sometimes I do not get available port. I tried to use Interlocked class in order to share last used port between multiple threads, but it didn't help.
Here's my tests base class:
public class BaseSteps
{
private const int PortRangeStart = 3368;
private const int PortRangeEnd = 8968;
private static long _portNumber = PortRangeStart;
private IDisposable _webServer;
//.....
[BeforeScenario]
public void Before()
{
Url = GetFullUrl();
_webServer = WebApp.Start<TestStartup>(Url);
}
[AfterScenario]
public void After()
{
_webServer.Dispose();
}
private static string GetFullUrl()
{
var ipAddress = IPAddress.Loopback;
var portAvailable = GetAvailablePort(PortRangeStart, PortRangeEnd, ipAddress);
return String.Format("http://{0}:{1}/", "localhost", portAvailable);
}
private static int GetAvailablePort(int rangeStart, int rangeEnd, IPAddress ip, bool includeIdlePorts = false)
{
IPGlobalProperties ipProps = IPGlobalProperties.GetIPGlobalProperties();
// if the ip we want a port on is an 'any' or loopback port we need to exclude all ports that are active on any IP
Func<IPAddress, bool> isIpAnyOrLoopBack = i => IPAddress.Any.Equals(i) ||
IPAddress.IPv6Any.Equals(i) ||
IPAddress.Loopback.Equals(i) ||
IPAddress.IPv6Loopback.
Equals(i);
// get all active ports on specified IP.
List<ushort> excludedPorts = new List<ushort>();
// if a port is open on an 'any' or 'loopback' interface then include it in the excludedPorts
excludedPorts.AddRange(from n in ipProps.GetActiveTcpConnections()
where
n.LocalEndPoint.Port >= rangeStart &&
n.LocalEndPoint.Port <= rangeEnd && (
isIpAnyOrLoopBack(ip) || n.LocalEndPoint.Address.Equals(ip) ||
isIpAnyOrLoopBack(n.LocalEndPoint.Address)) &&
(!includeIdlePorts || n.State != TcpState.TimeWait)
select (ushort)n.LocalEndPoint.Port);
excludedPorts.AddRange(from n in ipProps.GetActiveTcpListeners()
where n.Port >= rangeStart && n.Port <= rangeEnd && (
isIpAnyOrLoopBack(ip) || n.Address.Equals(ip) || isIpAnyOrLoopBack(n.Address))
select (ushort)n.Port);
excludedPorts.AddRange(from n in ipProps.GetActiveUdpListeners()
where n.Port >= rangeStart && n.Port <= rangeEnd && (
isIpAnyOrLoopBack(ip) || n.Address.Equals(ip) || isIpAnyOrLoopBack(n.Address))
select (ushort)n.Port);
excludedPorts.Sort();
for (int port = rangeStart; port <= rangeEnd; port++)
{
if (!excludedPorts.Contains((ushort)port) && Interlocked.Read(ref _portNumber) < port)
{
Interlocked.Increment(ref _portNumber);
return port;
}
}
return 0;
}
}
Does anyone know how to make sure, that I always get available port?