From 68ffcda53be5160cc9c94f27ea23c42441c12406 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sat, 14 Mar 2020 08:51:26 +0100 Subject: [PATCH] Let the .NET core/standard WebHostBuilder use a random port (#417) * wip * code reformat --- src/WireMock.Net/Owin/AspNetCoreSelfHost.cs | 64 +++++++++++-------- src/WireMock.Net/Owin/HostUrlOptions.cs | 41 ++++++++++++ src/WireMock.Net/Owin/OwinSelfHost.cs | 23 +++---- src/WireMock.Net/Server/WireMockServer.cs | 21 ++++-- .../ObservableLogEntriesTest.cs | 2 +- .../WireMock.Net.Tests.csproj | 5 +- 6 files changed, 105 insertions(+), 51 deletions(-) create mode 100644 src/WireMock.Net/Owin/HostUrlOptions.cs diff --git a/src/WireMock.Net/Owin/AspNetCoreSelfHost.cs b/src/WireMock.Net/Owin/AspNetCoreSelfHost.cs index 595bd3f2..d39f1fc3 100644 --- a/src/WireMock.Net/Owin/AspNetCoreSelfHost.cs +++ b/src/WireMock.Net/Owin/AspNetCoreSelfHost.cs @@ -20,10 +20,10 @@ namespace WireMock.Owin { private readonly CancellationTokenSource _cts = new CancellationTokenSource(); private readonly IWireMockMiddlewareOptions _options; - private readonly string[] _urls; private readonly IWireMockLogger _logger; - private Exception _runningException; + private readonly HostUrlOptions _urlOptions; + private Exception _runningException; private IWebHost _host; public bool IsStarted { get; private set; } @@ -34,23 +34,15 @@ namespace WireMock.Owin public Exception RunningException => _runningException; - public AspNetCoreSelfHost([NotNull] IWireMockMiddlewareOptions options, [NotNull] params string[] uriPrefixes) + public AspNetCoreSelfHost([NotNull] IWireMockMiddlewareOptions options, [NotNull] HostUrlOptions urlOptions) { Check.NotNull(options, nameof(options)); - Check.NotNullOrEmpty(uriPrefixes, nameof(uriPrefixes)); + Check.NotNull(urlOptions, nameof(urlOptions)); _logger = options.Logger ?? new WireMockConsoleLogger(); - foreach (string uriPrefix in uriPrefixes) - { - Urls.Add(uriPrefix); - - PortUtils.TryExtract(uriPrefix, out string protocol, out string host, out int port); - Ports.Add(port); - } - _options = options; - _urls = uriPrefixes; + _urlOptions = urlOptions; } public Task StartAsync() @@ -86,31 +78,35 @@ namespace WireMock.Owin }) .UseKestrel(options => { + var urlDetails = _urlOptions.GetDetails(); + #if NETSTANDARD1_3 - if (_urls.Any(u => u.StartsWith("https://", StringComparison.OrdinalIgnoreCase))) + + var urls = urlDetails.Select(u => u.Url); + if (urls.Any(u => u.StartsWith("https://", StringComparison.OrdinalIgnoreCase))) { options.UseHttps(PublicCertificateHelper.GetX509Certificate2()); } #else - // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel?tabs=aspnetcore2x - foreach (string url in _urls.Where(u => u.StartsWith("http://", StringComparison.OrdinalIgnoreCase))) + foreach (var detail in urlDetails) { - PortUtils.TryExtract(url, out string protocol, out string host, out int port); - options.Listen(System.Net.IPAddress.Any, port); - } - - foreach (string url in _urls.Where(u => u.StartsWith("https://", StringComparison.OrdinalIgnoreCase))) - { - PortUtils.TryExtract(url, out string protocol, out string host, out int port); - options.Listen(System.Net.IPAddress.Any, port, listenOptions => + if (detail.Url.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) { - listenOptions.UseHttps(); // PublicCertificateHelper.GetX509Certificate2() - }); + options.Listen(System.Net.IPAddress.Any, detail.Port, listenOptions => + { + listenOptions.UseHttps(); // PublicCertificateHelper.GetX509Certificate2() + }); + } + else + { + options.Listen(System.Net.IPAddress.Any, detail.Port); + } } #endif }) + #if NETSTANDARD1_3 - .UseUrls(_urls) + .UseUrls(_urlOptions.GetDetails().Select(u => u.Url).ToArray()) #endif .Build(); @@ -124,6 +120,18 @@ namespace WireMock.Owin var appLifetime = (IApplicationLifetime)_host.Services.GetService(typeof(IApplicationLifetime)); appLifetime.ApplicationStarted.Register(() => { + var addresses = _host.ServerFeatures + .Get() + .Addresses; + + foreach (string address in addresses) + { + Urls.Add(address.Replace("0.0.0.0", "localhost")); + + PortUtils.TryExtract(address, out string protocol, out string host, out int port); + Ports.Add(port); + } + IsStarted = true; }); @@ -166,5 +174,5 @@ namespace WireMock.Owin #endif } } -} +} #endif \ No newline at end of file diff --git a/src/WireMock.Net/Owin/HostUrlOptions.cs b/src/WireMock.Net/Owin/HostUrlOptions.cs new file mode 100644 index 00000000..0df16a36 --- /dev/null +++ b/src/WireMock.Net/Owin/HostUrlOptions.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using WireMock.Util; + +namespace WireMock.Owin +{ + internal class HostUrlOptions + { + public ICollection Urls { get; set; } + + public bool UseSSL { get; set; } + + public ICollection<(string Url, int Port)> GetDetails() + { + var list = new List<(string Url, int Port)>(); + if (Urls == null) + { + int port = FindFreeTcpPort(); + list.Add(($"{(UseSSL ? "https" : "http")}://localhost:{port}", port)); + } + else + { + foreach (string url in Urls) + { + PortUtils.TryExtract(url, out string protocol, out string host, out int port); + list.Add((url, port)); + } + } + + return list; + } + + private int FindFreeTcpPort() + { +#if USE_ASPNETCORE || NETSTANDARD2_0 + return 0; +#else + return PortUtils.FindFreeTcpPort(); +#endif + } + } +} \ No newline at end of file diff --git a/src/WireMock.Net/Owin/OwinSelfHost.cs b/src/WireMock.Net/Owin/OwinSelfHost.cs index e312d3d3..f9610cb3 100644 --- a/src/WireMock.Net/Owin/OwinSelfHost.cs +++ b/src/WireMock.Net/Owin/OwinSelfHost.cs @@ -8,7 +8,6 @@ using System.Threading; using System.Threading.Tasks; using WireMock.Logging; using WireMock.Owin.Mappers; -using WireMock.Util; using WireMock.Validation; namespace WireMock.Owin @@ -18,24 +17,22 @@ namespace WireMock.Owin private readonly IWireMockMiddlewareOptions _options; private readonly CancellationTokenSource _cts = new CancellationTokenSource(); private readonly IWireMockLogger _logger; + private Exception _runningException; - public OwinSelfHost([NotNull] IWireMockMiddlewareOptions options, [NotNull] params string[] uriPrefixes) + public OwinSelfHost([NotNull] IWireMockMiddlewareOptions options, [NotNull] HostUrlOptions urlOptions) { Check.NotNull(options, nameof(options)); - Check.NotNullOrEmpty(uriPrefixes, nameof(uriPrefixes)); - - _logger = options.Logger ?? new WireMockConsoleLogger(); - - foreach (string uriPrefix in uriPrefixes) - { - Urls.Add(uriPrefix); - - PortUtils.TryExtract(uriPrefix, out string protocol, out string host, out int port); - Ports.Add(port); - } + Check.NotNull(urlOptions, nameof(urlOptions)); _options = options; + _logger = options.Logger ?? new WireMockConsoleLogger(); + + foreach (var detail in urlOptions.GetDetails()) + { + Urls.Add(detail.Url); + Ports.Add(detail.Port); + } } public bool IsStarted { get; private set; } diff --git a/src/WireMock.Net/Server/WireMockServer.cs b/src/WireMock.Net/Server/WireMockServer.cs index 891203da..9eaaad8f 100644 --- a/src/WireMock.Net/Server/WireMockServer.cs +++ b/src/WireMock.Net/Server/WireMockServer.cs @@ -202,14 +202,20 @@ namespace WireMock.Server _settings.Logger.Info("WireMock.Net by Stef Heyenrath (https://github.com/WireMock-Net/WireMock.Net)"); _settings.Logger.Debug("WireMock.Net server settings {0}", JsonConvert.SerializeObject(settings, Formatting.Indented)); + HostUrlOptions urlOptions; if (settings.Urls != null) { - Urls = settings.Urls.ToArray(); + urlOptions = new HostUrlOptions + { + Urls = settings.Urls + }; } else { - int port = settings.Port > 0 ? settings.Port.Value : PortUtils.FindFreeTcpPort(); - Urls = new[] { $"{(settings.UseSSL == true ? "https" : "http")}://localhost:{port}" }; + urlOptions = new HostUrlOptions + { + UseSSL = settings.UseSSL == true + }; } _options.FileSystemHandler = _settings.FileSystemHandler; @@ -222,12 +228,10 @@ namespace WireMock.Server _mappingConverter = new MappingConverter(_matcherMapper); #if USE_ASPNETCORE - _httpServer = new AspNetCoreSelfHost(_options, Urls); + _httpServer = new AspNetCoreSelfHost(_options, urlOptions); #else - _httpServer = new OwinSelfHost(_options, Urls); + _httpServer = new OwinSelfHost(_options, urlOptions); #endif - Ports = _httpServer.Ports; - var startTask = _httpServer.StartAsync(); using (var ctsStartTimeout = new CancellationTokenSource(settings.StartTimeout)) @@ -254,6 +258,9 @@ namespace WireMock.Server ctsStartTimeout.Token.WaitHandle.WaitOne(ServerStartDelayInMs); } + + Urls = _httpServer.Urls.ToArray(); + Ports = _httpServer.Ports; } if (settings.AllowBodyForAllHttpMethods == true) diff --git a/test/WireMock.Net.Tests/ObservableLogEntriesTest.cs b/test/WireMock.Net.Tests/ObservableLogEntriesTest.cs index 8252c85d..be93fcec 100644 --- a/test/WireMock.Net.Tests/ObservableLogEntriesTest.cs +++ b/test/WireMock.Net.Tests/ObservableLogEntriesTest.cs @@ -96,7 +96,7 @@ namespace WireMock.Net.Tests var listOfTasks = new List>(); for (var i = 0; i < expectedCount; i++) { - Thread.Sleep(10); + Thread.Sleep(50); listOfTasks.Add(http.GetAsync($"{server.Urls[0]}{path}")); } var responses = await Task.WhenAll(listOfTasks); diff --git a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj index a225c8ee..b0bade62 100644 --- a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj +++ b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj @@ -2,8 +2,8 @@ Stef Heyenrath - net452;netcoreapp2.1 - + + netcoreapp2.1 full WireMock.Net.Tests WireMock.Net.Tests @@ -58,6 +58,7 @@ +