diff --git a/src/WireMock.Net/Owin/AspNetCoreSelfHost.NETStandard.cs b/src/WireMock.Net/Owin/AspNetCoreSelfHost.NETStandard.cs index 26536ece..0cbbed20 100644 --- a/src/WireMock.Net/Owin/AspNetCoreSelfHost.NETStandard.cs +++ b/src/WireMock.Net/Owin/AspNetCoreSelfHost.NETStandard.cs @@ -75,8 +75,15 @@ private static void SetHttpsAndUrls(KestrelServerOptions kestrelOptions, IWireMo private static void Listen(KestrelServerOptions kestrelOptions, HostUrlDetails urlDetail, Action configure) { + // Listens on any IP with the given port. + if (urlDetail is { Port: > 0, Host: "0.0.0.0" }) + { + kestrelOptions.ListenAnyIP(urlDetail.Port, configure); + return; + } + // Listens on ::1 and 127.0.0.1 with the given port. - if (urlDetail is { Port: > 0, Host: "localhost" or "127.0.0.1" or "0.0.0.0" or "::1" }) + if (urlDetail is { Port: > 0, Host: "localhost" or "127.0.0.1" or "::1" }) { kestrelOptions.ListenLocalhost(urlDetail.Port, configure); return; @@ -113,4 +120,4 @@ internal static IWebHostBuilder ConfigureKestrelServerOptions(this IWebHostBuild } } } -#endif \ No newline at end of file +#endif diff --git a/test/WireMock.Net.Tests/Facts/IgnoreOnContinuousIntegrationFact.cs b/test/WireMock.Net.Tests/Facts/IgnoreOnContinuousIntegrationFact.cs new file mode 100644 index 00000000..d1254d94 --- /dev/null +++ b/test/WireMock.Net.Tests/Facts/IgnoreOnContinuousIntegrationFact.cs @@ -0,0 +1,22 @@ +// Copyright © WireMock.Net + +using System; +using Xunit; + +namespace WireMock.Net.Tests.Facts; + +public sealed class IgnoreOnContinuousIntegrationFact : FactAttribute +{ + private static readonly string _skipReason = "Ignore when run via CI/CD"; + private static readonly bool _isContinuousIntegrationAzure = bool.TryParse(Environment.GetEnvironmentVariable("TF_BUILD"), out var isTF) && isTF; + private static readonly bool _isContinuousIntegrationGithub = bool.TryParse(Environment.GetEnvironmentVariable("GITHUB_ACTIONS"), out var isGH) && isGH; + private static bool IsContinuousIntegration() => _isContinuousIntegrationAzure || _isContinuousIntegrationGithub; + + public IgnoreOnContinuousIntegrationFact() + { + if (IsContinuousIntegration()) + { + Skip = _skipReason; + } + } +} diff --git a/test/WireMock.Net.Tests/WireMockServerTests.cs b/test/WireMock.Net.Tests/WireMockServerTests.cs index c53a7aa3..dfe24c63 100644 --- a/test/WireMock.Net.Tests/WireMockServerTests.cs +++ b/test/WireMock.Net.Tests/WireMockServerTests.cs @@ -8,6 +8,8 @@ using System.Net; using System.Net.Http; using System.Net.Http.Headers; +using System.Net.NetworkInformation; +using System.Net.Sockets; using System.Text; using System.Threading.Tasks; using FluentAssertions; @@ -16,6 +18,7 @@ using WireMock.Admin.Mappings; using WireMock.Http; using WireMock.Matchers; +using WireMock.Net.Tests.Facts; using WireMock.Net.Tests.Serialization; using WireMock.Net.Xunit; using WireMock.RequestBuilders; @@ -37,7 +40,7 @@ public WireMockServerTests(ITestOutputHelper testOutputHelper) { _testOutputHelper = testOutputHelper; } - + [Fact] public void WireMockServer_Start() { @@ -196,6 +199,70 @@ public async Task WireMockServer_When_HttpClientWithWebProxyCallsHttp_Should_Wor } #endif +#if NET6_0_OR_GREATER + private static string[] GetIPAddressesByFamily(AddressFamily addressFamily) + { + return NetworkInterface.GetAllNetworkInterfaces() + .Where(ni => ni.OperationalStatus == OperationalStatus.Up) + .SelectMany(ni => ni.GetIPProperties().UnicastAddresses) + .Where(addr => addr.Address.AddressFamily == addressFamily) + .Select(addr => addr.Address.ToString()) + .ToArray(); + } + + [IgnoreOnContinuousIntegrationFact] + public async Task WireMockServer_WithUrl0000_Should_Listen_On_All_IPs_IPv4() + { + // Arrange + var port = PortUtils.FindFreeTcpPort(); + var IPv4 = GetIPAddressesByFamily(System.Net.Sockets.AddressFamily.InterNetwork); + var settings = new WireMockServerSettings + { + Urls = new string[] { "http://0.0.0.0:" + port }, + }; + var server = WireMockServer.Start(settings); + + server.Given(Request.Create().WithPath("/*")).RespondWith(Response.Create().WithBody("x")); + + foreach (var addr in IPv4) + { + // Act + var response = await new HttpClient().GetStringAsync("http://" + addr + ":" + server.Ports[0] + "/foo").ConfigureAwait(false); + + // Assert + response.Should().Be("x"); + } + + server.Stop(); + } + + [IgnoreOnContinuousIntegrationFact] + public async Task WireMockServer_WithUrl0000_Should_Listen_On_All_IPs_IPv6() + { + // Arrange + var port = PortUtils.FindFreeTcpPort(); + var IPv6 = GetIPAddressesByFamily(System.Net.Sockets.AddressFamily.InterNetworkV6); + var settings = new WireMockServerSettings + { + Urls = new string[] { "http://0.0.0.0:" + port }, + }; + var server = WireMockServer.Start(settings); + + server.Given(Request.Create().WithPath("/*")).RespondWith(Response.Create().WithBody("x")); + + foreach (var addr in IPv6) + { + // Act + var response = await new HttpClient().GetStringAsync("http://[" + addr + "]:" + server.Ports[0] + "/foo").ConfigureAwait(false); + + // Assert + response.Should().Be("x"); + } + + server.Stop(); + } +#endif + [Fact] public async Task WireMockServer_Should_respond_a_redirect_without_body() { @@ -248,7 +315,7 @@ public async Task WireMockServer_WithCorsPolicyOptions_Should_Work_Correct() // Act var response = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/foo").ConfigureAwait(false); - // Asser. + // Assert response.Should().Be("x"); server.Stop(); @@ -274,7 +341,7 @@ public async Task WireMockServer_Should_delay_responses_for_a_given_route() await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/foo").ConfigureAwait(false); watch.Stop(); - // Asser. + // Assert watch.ElapsedMilliseconds.Should().BeGreaterOrEqualTo(0); server.Stop(); @@ -662,4 +729,4 @@ public async Task WireMockServer_Using_JsonMapping_And_CustomMatcher_WithIncorre server.Stop(); } #endif -} \ No newline at end of file +}