diff --git a/src/WireMock.Net.Minimal/Util/PortUtils.cs b/src/WireMock.Net.Minimal/Util/PortUtils.cs index 8281fb940..6ef513861 100644 --- a/src/WireMock.Net.Minimal/Util/PortUtils.cs +++ b/src/WireMock.Net.Minimal/Util/PortUtils.cs @@ -1,7 +1,6 @@ // Copyright © WireMock.Net using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Net; using System.Net.Sockets; using System.Text.RegularExpressions; @@ -14,7 +13,7 @@ namespace WireMock.Util; /// internal static class PortUtils { - private static readonly Regex UrlDetailsRegex = new(@"^((?\w+)://)(?[^/]+?):(?\d+)\/?$", RegexOptions.Compiled, RegexConstants.DefaultTimeout); + private static readonly Regex UrlDetailsRegex = new(@"^((?\w+)://)(?[^/]+?)(:(?\d+))?\/?$", RegexOptions.Compiled, RegexConstants.DefaultTimeout); /// /// Finds a random, free port to be listened on. @@ -90,17 +89,44 @@ public static bool TryExtract(string url, out bool isHttps, out bool isHttp2, [N host = null; port = 0; - var match = UrlDetailsRegex.Match(url); - if (match.Success) + var urlDetails = ExtractUrlDetails(url); + + if (urlDetails is null || urlDetails.Value.Port < 0) { - scheme = match.Groups["scheme"].Value; - isHttps = scheme.StartsWith("https", StringComparison.OrdinalIgnoreCase) || scheme.StartsWith("grpcs", StringComparison.OrdinalIgnoreCase) || scheme.StartsWith("wss", StringComparison.OrdinalIgnoreCase); - isHttp2 = scheme.StartsWith("grpc", StringComparison.OrdinalIgnoreCase); - host = match.Groups["host"].Value; + return false; + } + + scheme = urlDetails.Value.Scheme; + isHttps = scheme.StartsWith("https", StringComparison.OrdinalIgnoreCase) || scheme.StartsWith("grpcs", StringComparison.OrdinalIgnoreCase) || scheme.StartsWith("wss", StringComparison.OrdinalIgnoreCase); + isHttp2 = scheme.StartsWith("grpc", StringComparison.OrdinalIgnoreCase); + host = urlDetails.Value.Host; + port = urlDetails.Value.Port; - return int.TryParse(match.Groups["port"].Value, out port); + return true; + } + + private static (string Scheme, string Host, int Port)? ExtractUrlDetails(string url) + { + var match = UrlDetailsRegex.Match(url); + + if (!match.Success) + { + return null; } - return false; + var scheme = match.Groups["scheme"].Value; + var host = match.Groups["host"].Value; + var port = match.Groups["port"].Success && int.TryParse(match.Groups["port"].Value, out var parsedPort) + ? parsedPort + : MapPortFromScheme(scheme); + + return (scheme, host, port); } + + private static int MapPortFromScheme(string scheme) => scheme switch + { + "http" => 80, + "https" => 443, + _ => -1, + }; } \ No newline at end of file diff --git a/test/WireMock.Net.Tests/Util/PortUtilsTests.cs b/test/WireMock.Net.Tests/Util/PortUtilsTests.cs index 5b4607018..afe17433a 100644 --- a/test/WireMock.Net.Tests/Util/PortUtilsTests.cs +++ b/test/WireMock.Net.Tests/Util/PortUtilsTests.cs @@ -21,14 +21,14 @@ public void PortUtils_TryExtract_InvalidUrl_Returns_False() isGrpc.Should().BeFalse(); scheme.Should().BeNull(); host.Should().BeNull(); - port.Should().Be(default(int)); + port.Should().Be(default); } [Fact] - public void PortUtils_TryExtract_UrlIsMissingPort_Returns_False() + public void PortUtils_TryExtract_DefaultPort_UnknownScheme_Returns_False() { // Assign - var url = "http://0.0.0.0"; + var url = "grpc://0.0.0.0"; // Act var result = PortUtils.TryExtract(url, out var isHttps, out var isGrpc, out var scheme, out var host, out var port); @@ -39,7 +39,43 @@ public void PortUtils_TryExtract_UrlIsMissingPort_Returns_False() isGrpc.Should().BeFalse(); scheme.Should().BeNull(); host.Should().BeNull(); - port.Should().Be(default(int)); + port.Should().Be(default); + } + + [Fact] + public void PortUtils_TryExtract_DefaultPort_Http_Returns_True() + { + // Assign + var url = "http://0.0.0.0"; + + // Act + var result = PortUtils.TryExtract(url, out var isHttps, out var isGrpc, out var scheme, out var host, out var port); + + // Assert + result.Should().BeTrue(); + isHttps.Should().BeFalse(); + isGrpc.Should().BeFalse(); + scheme.Should().Be("http"); + host.Should().Be("0.0.0.0"); + port.Should().Be(80); + } + + [Fact] + public void PortUtils_TryExtract_DefaultPort_Https_Returns_True() + { + // Assign + var url = "https://0.0.0.0"; + + // Act + var result = PortUtils.TryExtract(url, out var isHttps, out var isGrpc, out var scheme, out var host, out var port); + + // Assert + result.Should().BeTrue(); + isHttps.Should().BeTrue(); + isGrpc.Should().BeFalse(); + scheme.Should().Be("https"); + host.Should().Be("0.0.0.0"); + port.Should().Be(443); } [Fact]