// Copyright © WireMock.Net using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text.RegularExpressions; using WireMock.Constants; namespace WireMock.Util; /// /// Port Utility class /// internal static class PortUtils { private static readonly Regex UrlDetailsRegex = new(@"^((?\w+)://)(?[^/]+?):(?\d+)\/?$", RegexOptions.Compiled, RegexConstants.DefaultTimeout); /// /// Finds a random, free port to be listened on. /// /// A random, free port to be listened on. /// https://github.com/SeleniumHQ/selenium/blob/trunk/dotnet/src/webdriver/Internal/PortUtilities.cs public static int FindFreeTcpPort() { // Locate a free port on the local machine by binding a socket to an IPEndPoint using IPAddress.Any and port 0. // The socket will select a free port. var portSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { var socketEndPoint = new IPEndPoint(IPAddress.Any, 0); portSocket.Bind(socketEndPoint); socketEndPoint = (IPEndPoint)portSocket.LocalEndPoint!; return socketEndPoint.Port; } finally { #if !NETSTANDARD1_3 portSocket.Close(); #endif portSocket.Dispose(); } } /// /// Finds a specified number of random, free ports to be listened on. /// /// The number of free ports to find. /// A list of random, free ports to be listened on. public static IReadOnlyList FindFreeTcpPorts(int count) { var sockets = Enumerable .Range(0, count) .Select(_ => new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) .ToArray(); var freePorts = new List(); try { foreach (var socket in sockets) { var socketEndPoint = new IPEndPoint(IPAddress.Any, 0); socket.Bind(socketEndPoint); socketEndPoint = (IPEndPoint)socket.LocalEndPoint!; freePorts.Add(socketEndPoint.Port); } return freePorts; } finally { foreach (var socket in sockets) { #if !NETSTANDARD1_3 socket.Close(); #endif socket.Dispose(); } } } /// /// Extract the isHttps, isHttp2, scheme, host and port from a URL. /// public static bool TryExtract(string url, out bool isHttps, out bool isHttp2, [NotNullWhen(true)] out string? scheme, [NotNullWhen(true)] out string? host, out int port) { isHttps = false; isHttp2 = false; scheme = null; host = null; port = 0; var match = UrlDetailsRegex.Match(url); if (match.Success) { scheme = match.Groups["proto"].Value; isHttps = scheme.StartsWith("https", StringComparison.OrdinalIgnoreCase) || scheme.StartsWith("grpcs", StringComparison.OrdinalIgnoreCase); isHttp2 = scheme.StartsWith("grpc", StringComparison.OrdinalIgnoreCase); host = match.Groups["host"].Value; return int.TryParse(match.Groups["port"].Value, out port); } return false; } }