diff --git a/Directory.Build.props b/Directory.Build.props
index 86955e73..1df62db2 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -15,6 +15,7 @@
PackageReadme.md
12
enable
+ enable
diff --git a/src/WireMock.Net.Abstractions/Types/HostingScheme.cs b/src/WireMock.Net.Abstractions/Types/HostingScheme.cs
index beaaa11f..6ee89b9f 100644
--- a/src/WireMock.Net.Abstractions/Types/HostingScheme.cs
+++ b/src/WireMock.Net.Abstractions/Types/HostingScheme.cs
@@ -1,7 +1,5 @@
// Copyright © WireMock.Net
-using System;
-
namespace WireMock.Types;
[Flags]
@@ -13,5 +11,11 @@ public enum HostingScheme
Https = 0x2,
- HttpAndHttps = Http | Https
+ HttpAndHttps = Http | Https,
+
+ Ws = 0x4,
+
+ Wss = 0x8,
+
+ WsAndWss = Ws | Wss
}
\ No newline at end of file
diff --git a/src/WireMock.Net.Minimal/Owin/AspNetCoreSelfHost.cs b/src/WireMock.Net.Minimal/Owin/AspNetCoreSelfHost.cs
index bb696c2d..ae5b1c6f 100644
--- a/src/WireMock.Net.Minimal/Owin/AspNetCoreSelfHost.cs
+++ b/src/WireMock.Net.Minimal/Owin/AspNetCoreSelfHost.cs
@@ -1,10 +1,5 @@
// Copyright © WireMock.Net
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
@@ -27,9 +22,9 @@ internal partial class AspNetCoreSelfHost : IOwinSelfHost
public bool IsStarted { get; private set; }
- public List Urls { get; } = new();
+ public List Urls { get; } = [];
- public List Ports { get; } = new();
+ public List Ports { get; } = [];
public Exception? RunningException { get; private set; }
@@ -120,14 +115,42 @@ internal partial class AspNetCoreSelfHost : IOwinSelfHost
{
var addresses = _host.ServerFeatures
.Get()!
- .Addresses;
+ .Addresses
+ .ToArray();
- foreach (var address in addresses)
+ if (_urlOptions.Urls == null)
{
- Urls.Add(address.Replace("0.0.0.0", "localhost").Replace("[::]", "localhost"));
+ foreach (var address in addresses)
+ {
+ PortUtils.TryExtract(address, out _, out _, out var scheme, out var host, out var port);
- PortUtils.TryExtract(address, out _, out _, out _, out _, out var port);
- Ports.Add(port);
+ var replacedHost = ReplaceHostWithLocalhost(host!);
+ var newUrl = $"{scheme}://{replacedHost}:{port}";
+ Urls.Add(newUrl);
+ Ports.Add(port);
+ }
+ }
+ else
+ {
+ var urlOptions = _urlOptions?.Urls?.ToArray() ?? [];
+
+ for (int i = 0; i < urlOptions.Length; i++)
+ {
+ PortUtils.TryExtract(urlOptions[i], out _, out _, out var originalScheme, out _, out _);
+ if (originalScheme!.StartsWith("grpc", StringComparison.OrdinalIgnoreCase))
+ {
+ // Always replace "grpc" with "http" in the scheme because GrpcChannel needs http or https.
+ originalScheme = originalScheme.Replace("grpc", "http", StringComparison.OrdinalIgnoreCase);
+ }
+
+ PortUtils.TryExtract(addresses[i], out _, out _, out _, out var realHost, out var realPort);
+
+ var replacedHost = ReplaceHostWithLocalhost(realHost!);
+ var newUrl = $"{originalScheme}://{replacedHost}:{realPort}";
+
+ Urls.Add(newUrl);
+ Ports.Add(realPort);
+ }
}
IsStarted = true;
@@ -159,4 +182,9 @@ internal partial class AspNetCoreSelfHost : IOwinSelfHost
IsStarted = false;
return _host.StopAsync();
}
+
+ private static string ReplaceHostWithLocalhost(string host)
+ {
+ return host.Replace("0.0.0.0", "localhost").Replace("[::]", "localhost");
+ }
}
\ No newline at end of file
diff --git a/src/WireMock.Net.Minimal/Owin/HostUrlOptions.cs b/src/WireMock.Net.Minimal/Owin/HostUrlOptions.cs
index 055e14d5..6e06aaa3 100644
--- a/src/WireMock.Net.Minimal/Owin/HostUrlOptions.cs
+++ b/src/WireMock.Net.Minimal/Owin/HostUrlOptions.cs
@@ -1,6 +1,5 @@
// Copyright © WireMock.Net
-using System.Collections.Generic;
using WireMock.Types;
using WireMock.Util;
@@ -23,20 +22,34 @@ internal class HostUrlOptions
var list = new List();
if (Urls == null)
{
- if (HostingScheme is HostingScheme.Http or HostingScheme.Https)
+ if (HostingScheme is not HostingScheme.None)
{
- var port = Port > 0 ? Port.Value : FindFreeTcpPort();
- var scheme = HostingScheme == HostingScheme.Https ? "https" : "http";
- list.Add(new HostUrlDetails { IsHttps = HostingScheme == HostingScheme.Https, IsHttp2 = UseHttp2 == true, Url = $"{scheme}://{Star}:{port}", Scheme = scheme, Host = Star, Port = port });
+ var scheme = GetSchemeAsString(HostingScheme);
+ var port = Port > 0 ? Port.Value : 0;
+ var isHttps = HostingScheme == HostingScheme.Https || HostingScheme == HostingScheme.Wss;
+ list.Add(new HostUrlDetails { IsHttps = isHttps, IsHttp2 = UseHttp2 == true, Url = $"{scheme}://{Star}:{port}", Scheme = scheme, Host = Star, Port = port });
}
if (HostingScheme == HostingScheme.HttpAndHttps)
{
- var httpPort = Port > 0 ? Port.Value : FindFreeTcpPort();
- list.Add(new HostUrlDetails { IsHttps = false, IsHttp2 = UseHttp2 == true, Url = $"http://{Star}:{httpPort}", Scheme = "http", Host = Star, Port = httpPort });
+ var port = Port > 0 ? Port.Value : 0;
+ var scheme = GetSchemeAsString(HostingScheme.Http);
+ list.Add(new HostUrlDetails { IsHttps = false, IsHttp2 = UseHttp2 == true, Url = $"{scheme}://{Star}:{port}", Scheme = scheme, Host = Star, Port = port });
- var httpsPort = FindFreeTcpPort(); // In this scenario, always get a free port for https.
- list.Add(new HostUrlDetails { IsHttps = true, IsHttp2 = UseHttp2 == true, Url = $"https://{Star}:{httpsPort}", Scheme = "https", Host = Star, Port = httpsPort });
+ var securePort = 0; // In this scenario, always get a free port for https.
+ var secureScheme = GetSchemeAsString(HostingScheme.Https);
+ list.Add(new HostUrlDetails { IsHttps = true, IsHttp2 = UseHttp2 == true, Url = $"{secureScheme}://{Star}:{securePort}", Scheme = secureScheme, Host = Star, Port = securePort });
+ }
+
+ if (HostingScheme == HostingScheme.WsAndWss)
+ {
+ var port = Port > 0 ? Port.Value : 0;
+ var scheme = GetSchemeAsString(HostingScheme.Ws);
+ list.Add(new HostUrlDetails { IsHttps = false, IsHttp2 = UseHttp2 == true, Url = $"{scheme}://{Star}:{port}", Scheme = scheme, Host = Star, Port = port });
+
+ var securePort = 0; // In this scenario, always get a free port for https.
+ var secureScheme = GetSchemeAsString(HostingScheme.Wss);
+ list.Add(new HostUrlDetails { IsHttps = true, IsHttp2 = UseHttp2 == true, Url = $"{secureScheme}://{Star}:{securePort}", Scheme = secureScheme, Host = Star, Port = securePort });
}
}
else
@@ -53,12 +66,19 @@ internal class HostUrlOptions
return list;
}
- private static int FindFreeTcpPort()
+ private string GetSchemeAsString(HostingScheme scheme)
{
-//#if USE_ASPNETCORE || NETSTANDARD2_0 || NETSTANDARD2_1
- return 0;
-//#else
- //return PortUtils.FindFreeTcpPort();
-//#endif
+ return scheme switch
+ {
+ HostingScheme.Http => "http",
+ HostingScheme.Https => "https",
+ HostingScheme.HttpAndHttps => "http", // Default to http when both are specified, since the https URL will be added separately with a free port.
+
+ HostingScheme.Ws => "ws",
+ HostingScheme.Wss => "wss",
+ HostingScheme.WsAndWss => "ws", // Default to ws when both are specified, since the wss URL will be added separately with a free port.
+
+ _ => throw new NotSupportedException($"Unsupported hosting scheme: {HostingScheme}")
+ };
}
}
\ No newline at end of file
diff --git a/src/WireMock.Net.Minimal/Util/PortUtils.cs b/src/WireMock.Net.Minimal/Util/PortUtils.cs
index a4465725..ef8e3560 100644
--- a/src/WireMock.Net.Minimal/Util/PortUtils.cs
+++ b/src/WireMock.Net.Minimal/Util/PortUtils.cs
@@ -1,9 +1,6 @@
// 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;
@@ -16,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.
@@ -37,9 +34,7 @@ internal static class PortUtils
}
finally
{
-//#if !NETSTANDARD1_3
portSocket.Close();
-//#endif
portSocket.Dispose();
}
}
@@ -75,9 +70,7 @@ internal static class PortUtils
{
foreach (var socket in sockets)
{
-//#if !NETSTANDARD1_3
socket.Close();
-//#endif
socket.Dispose();
}
}
@@ -97,8 +90,8 @@ internal static class PortUtils
var match = UrlDetailsRegex.Match(url);
if (match.Success)
{
- scheme = match.Groups["proto"].Value;
- isHttps = scheme.StartsWith("https", StringComparison.OrdinalIgnoreCase) || scheme.StartsWith("grpcs", StringComparison.OrdinalIgnoreCase);
+ 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;
diff --git a/src/WireMock.Net.Minimal/WebSockets/WebSocketBuilder.cs b/src/WireMock.Net.Minimal/WebSockets/WebSocketBuilder.cs
index 6f24ca80..7dc8a8e8 100644
--- a/src/WireMock.Net.Minimal/WebSockets/WebSocketBuilder.cs
+++ b/src/WireMock.Net.Minimal/WebSockets/WebSocketBuilder.cs
@@ -64,16 +64,14 @@ internal class WebSocketBuilder : IWebSocketBuilder
return this;
}
- public IWebSocketBuilder WithMessageHandler(
- Func handler)
+ public IWebSocketBuilder WithMessageHandler(Func handler)
{
MessageHandler = Guard.NotNull(handler);
IsEcho = false; // Disable echo if custom handler is set
return this;
}
- public IWebSocketBuilder WithMessageSequence(
- Action configure)
+ public IWebSocketBuilder WithMessageSequence(Action configure)
{
var sequenceBuilder = new WebSocketMessageSequenceBuilder();
configure(sequenceBuilder);
diff --git a/test/WireMock.Net.Tests/WebSockets/WebSocketIntegrationTests.cs b/test/WireMock.Net.Tests/WebSockets/WebSocketIntegrationTests.cs
index 273e0507..cbb2448a 100644
--- a/test/WireMock.Net.Tests/WebSockets/WebSocketIntegrationTests.cs
+++ b/test/WireMock.Net.Tests/WebSockets/WebSocketIntegrationTests.cs
@@ -1,12 +1,7 @@
// Copyright © WireMock.Net
-using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Net.WebSockets;
using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
using FluentAssertions;
using Newtonsoft.Json.Linq;
using WireMock.Net.Xunit;
@@ -14,7 +9,6 @@ using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Server;
using WireMock.Settings;
-using Xunit;
using Xunit.Abstractions;
namespace WireMock.Net.Tests.WebSockets;
@@ -34,7 +28,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
- Logger = new TestOutputHelperWireMockLogger(_output)
+ Logger = new TestOutputHelperWireMockLogger(_output),
+ Urls = ["ws://localhost:0"]
});
server
@@ -43,14 +38,13 @@ public class WebSocketIntegrationTests
.WithWebSocketUpgrade()
)
.RespondWith(Response.Create()
- .WithHeader("x", "y")
.WithWebSocket(ws => ws
.WithEcho()
)
);
using var client = new ClientWebSocket();
- var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/echo");
+ var uri = new Uri($"{server.Url!}/ws/echo");
// Act
await client.ConnectAsync(uri, CancellationToken.None);
@@ -78,7 +72,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
- Logger = new TestOutputHelperWireMockLogger(_output)
+ Logger = new TestOutputHelperWireMockLogger(_output),
+ Urls = ["ws://localhost:0"]
});
server
@@ -91,7 +86,7 @@ public class WebSocketIntegrationTests
);
using var client = new ClientWebSocket();
- var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/echo");
+ var uri = new Uri($"{server.Url!}/ws/echo");
await client.ConnectAsync(uri, CancellationToken.None);
var testMessages = new[] { "Hello", "World", "WebSocket", "Test" };
@@ -118,7 +113,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
- Logger = new TestOutputHelperWireMockLogger(_output)
+ Logger = new TestOutputHelperWireMockLogger(_output),
+ Urls = ["ws://localhost:0"]
});
server
@@ -131,7 +127,7 @@ public class WebSocketIntegrationTests
);
using var client = new ClientWebSocket();
- var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/echo");
+ var uri = new Uri($"{server.Url!}/ws/echo");
await client.ConnectAsync(uri, CancellationToken.None);
var testData = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 };
@@ -157,7 +153,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
- Logger = new TestOutputHelperWireMockLogger(_output)
+ Logger = new TestOutputHelperWireMockLogger(_output),
+ Urls = ["ws://localhost:0"]
});
server
@@ -170,7 +167,7 @@ public class WebSocketIntegrationTests
);
using var client = new ClientWebSocket();
- var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/echo");
+ var uri = new Uri($"{server.Url!}/ws/echo");
await client.ConnectAsync(uri, CancellationToken.None);
// Act
@@ -192,7 +189,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
- Logger = new TestOutputHelperWireMockLogger(_output)
+ Logger = new TestOutputHelperWireMockLogger(_output),
+ Urls = ["ws://localhost:0"]
});
server
@@ -218,7 +216,7 @@ public class WebSocketIntegrationTests
);
using var client = new ClientWebSocket();
- var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/chat");
+ var uri = new Uri($"{server.Url!}/ws/chat");
await client.ConnectAsync(uri, CancellationToken.None);
// Act
@@ -244,7 +242,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
- Logger = new TestOutputHelperWireMockLogger(_output)
+ Logger = new TestOutputHelperWireMockLogger(_output),
+ Urls = ["ws://localhost:0"]
});
server
@@ -288,7 +287,7 @@ public class WebSocketIntegrationTests
);
using var client = new ClientWebSocket();
- var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/chat");
+ var uri = new Uri($"{server.Url!}/ws/chat");
await client.ConnectAsync(uri, CancellationToken.None);
var commands = new (string, Action)[]
@@ -322,7 +321,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
- Logger = new TestOutputHelperWireMockLogger(_output)
+ Logger = new TestOutputHelperWireMockLogger(_output),
+ Urls = ["ws://localhost:0"]
});
server
@@ -348,7 +348,7 @@ public class WebSocketIntegrationTests
);
using var client = new ClientWebSocket();
- var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/json");
+ var uri = new Uri($"{server.Url!}/ws/json");
await client.ConnectAsync(uri, CancellationToken.None);
// Act
@@ -362,7 +362,7 @@ public class WebSocketIntegrationTests
// Assert
result.MessageType.Should().Be(WebSocketMessageType.Text);
-
+
var json = JObject.Parse(received);
json["message"]!.ToString().Should().Be(testMessage);
json["length"]!.Value().Should().Be(testMessage.Length);
@@ -378,7 +378,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
- Logger = new TestOutputHelperWireMockLogger(_output)
+ Logger = new TestOutputHelperWireMockLogger(_output),
+ Urls = ["ws://localhost:0"]
});
server
@@ -404,7 +405,7 @@ public class WebSocketIntegrationTests
);
using var client = new ClientWebSocket();
- var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/json");
+ var uri = new Uri($"{server.Url!}/ws/json");
await client.ConnectAsync(uri, CancellationToken.None);
var testMessages = new[] { "First", "Second", "Third" };
@@ -433,7 +434,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
- Logger = new TestOutputHelperWireMockLogger(_output)
+ Logger = new TestOutputHelperWireMockLogger(_output),
+ Urls = ["ws://localhost:0"]
});
server
@@ -470,7 +472,7 @@ public class WebSocketIntegrationTests
);
using var client = new ClientWebSocket();
- var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/json");
+ var uri = new Uri($"{server.Url!}/ws/json");
await client.ConnectAsync(uri, CancellationToken.None);
// Act
@@ -499,7 +501,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
- Logger = new TestOutputHelperWireMockLogger(_output)
+ Logger = new TestOutputHelperWireMockLogger(_output),
+ Urls = ["ws://localhost:0"]
});
var broadcastMappingGuid = Guid.NewGuid();
@@ -533,7 +536,7 @@ public class WebSocketIntegrationTests
using var client2 = new ClientWebSocket();
using var client3 = new ClientWebSocket();
- var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/broadcast");
+ var uri = new Uri($"{server.Url!}/ws/broadcast");
await client1.ConnectAsync(uri, CancellationToken.None);
await client2.ConnectAsync(uri, CancellationToken.None);
@@ -579,7 +582,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
- Logger = new TestOutputHelperWireMockLogger(_output)
+ Logger = new TestOutputHelperWireMockLogger(_output),
+ Urls = ["ws://localhost:0"]
});
var broadcastMappingGuid = Guid.NewGuid();
@@ -606,7 +610,7 @@ public class WebSocketIntegrationTests
using var client1 = new ClientWebSocket();
using var client2 = new ClientWebSocket();
- var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/broadcast");
+ var uri = new Uri($"{server.Url!}/ws/broadcast");
await client1.ConnectAsync(uri, CancellationToken.None);
await client2.ConnectAsync(uri, CancellationToken.None);
@@ -639,7 +643,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
- Logger = new TestOutputHelperWireMockLogger(_output)
+ Logger = new TestOutputHelperWireMockLogger(_output),
+ Urls = ["ws://localhost:0"]
});
var broadcastMappingGuid = Guid.NewGuid();
@@ -673,7 +678,7 @@ public class WebSocketIntegrationTests
using var client1 = new ClientWebSocket();
using var client2 = new ClientWebSocket();
- var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/broadcast-json");
+ var uri = new Uri($"{server.Url!}/ws/broadcast-json");
await client1.ConnectAsync(uri, CancellationToken.None);
await client2.ConnectAsync(uri, CancellationToken.None);
@@ -717,7 +722,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
- Logger = new TestOutputHelperWireMockLogger(_output)
+ Logger = new TestOutputHelperWireMockLogger(_output),
+ Urls = ["ws://localhost:0"]
});
var broadcastMappingGuid = Guid.NewGuid();
@@ -746,7 +752,7 @@ public class WebSocketIntegrationTests
using var client1 = new ClientWebSocket();
using var client2 = new ClientWebSocket();
- var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/broadcast");
+ var uri = new Uri($"{server.Url!}/ws/broadcast");
await client1.ConnectAsync(uri, CancellationToken.None);
await client2.ConnectAsync(uri, CancellationToken.None);
@@ -784,7 +790,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
- Logger = new TestOutputHelperWireMockLogger(_output)
+ Logger = new TestOutputHelperWireMockLogger(_output),
+ Urls = ["ws://localhost:0"]
});
var broadcastMappingGuid = Guid.NewGuid();
@@ -808,8 +815,8 @@ public class WebSocketIntegrationTests
)
);
- var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/broadcast");
- const int clientCount = 5;
+ var uri = new Uri($"{server.Url!}/ws/broadcast");
+ const int clientCount = 3;
var clients = new List();
try
@@ -822,7 +829,7 @@ public class WebSocketIntegrationTests
clients.Add(client);
}
- await Task.Delay(200); // Give time for all connections to register
+ await Task.Delay(100); // Give time for all connections to register
// Act - Send message from first client
var testMessage = "Mass broadcast";