diff options
author | James Newton-King <james@newtonking.com> | 2022-08-20 09:38:14 +0300 |
---|---|---|
committer | James Newton-King <james@newtonking.com> | 2022-08-20 09:38:14 +0300 |
commit | a9c2d4d42723ef70b2fda0542a06f3d35e52bba8 (patch) | |
tree | 6e8743c4745b73dc728fe5f3a3009fb29f79a395 | |
parent | e62c9c9f83254e8c0abf8dac5181859780882321 (diff) |
Rewrite retry helper to get ports via binding port to 0jamesnk/http3-flaky-test
-rw-r--r-- | src/Servers/Kestrel/shared/test/ServerRetryHelper.cs | 41 |
1 files changed, 37 insertions, 4 deletions
diff --git a/src/Servers/Kestrel/shared/test/ServerRetryHelper.cs b/src/Servers/Kestrel/shared/test/ServerRetryHelper.cs index 1466596c7b..d211dde1fd 100644 --- a/src/Servers/Kestrel/shared/test/ServerRetryHelper.cs +++ b/src/Servers/Kestrel/shared/test/ServerRetryHelper.cs @@ -1,28 +1,30 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Net; +using System.Net.Sockets; using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Testing; public static class ServerRetryHelper { - private const int RetryCount = 15; + private const int RetryCount = 10; /// <summary> /// Retry a func. Useful when a test needs an explicit port and you want to avoid port conflicts. /// </summary> public static async Task BindPortsWithRetry(Func<int, Task> retryFunc, ILogger logger) { + var ports = GetFreePorts(RetryCount); + var retryCount = 0; while (true) { - // Approx dynamic port range on Windows and Linux. - var randomPort = Random.Shared.Next(35000, 60000); try { - await retryFunc(randomPort); + await retryFunc(ports[retryCount]); break; } catch (Exception ex) @@ -40,4 +42,35 @@ public static class ServerRetryHelper } } } + + private static int[] GetFreePorts(int count) + { + var sockets = new List<Socket>(); + + for (var i = 0; i < count; i++) + { + // Find a port that's free by binding port 0. + // Note that this port should be free when the test runs, but: + // - Something else could steal it before the test uses it. + // - UDP port with the same number could be in use. + // For that reason, some retries should be available. + var ipEndPoint = new IPEndPoint(IPAddress.Loopback, 0); + var listenSocket = new Socket(ipEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + + listenSocket.Bind(ipEndPoint); + + sockets.Add(listenSocket); + } + + // Ports are calculated upfront. Rebinding with port 0 could result the same port + // being returned for each retry. + var ports = sockets.Select(s => (IPEndPoint)s.LocalEndPoint).Select(ep => ep.Port).ToArray(); + + foreach (var socket in sockets) + { + socket.Dispose(); + } + + return ports; + } } |