From c0b18631a3a850e78e17f7118190afb1eb2e3bbe Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 26 Sep 2022 14:24:45 +0200 Subject: [PATCH] Add option to run the server on http & https (#818) * HostingProtocol.HttpAndHttps * . * . * . * HostingScheme --- .../MainApp.cs | 13 ++++++++ .../Properties/launchSettings.json | 18 ++++++++--- .../Types/HostingScheme.cs | 15 +++++++++ src/WireMock.Net/Owin/HostUrlDetails.cs | 5 ++- src/WireMock.Net/Owin/HostUrlOptions.cs | 31 ++++++++++++++----- src/WireMock.Net/Server/WireMockServer.cs | 22 ++++++++++--- .../Settings/SimpleCommandLineParser.cs | 20 ++++++++++++ .../Settings/WireMockServerSettings.cs | 9 +++++- .../Settings/WireMockServerSettingsParser.cs | 13 +++----- 9 files changed, 118 insertions(+), 28 deletions(-) create mode 100644 src/WireMock.Net.Abstractions/Types/HostingScheme.cs diff --git a/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs b/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs index bfcce9dc..543e13da 100644 --- a/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs +++ b/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs @@ -40,6 +40,19 @@ namespace WireMock.Net.ConsoleApplication var s = WireMockServer.Start(); s.Stop(); + var httpAndHttpsWithPort = WireMockServer.Start(new WireMockServerSettings + { + HostingScheme = HostingScheme.HttpAndHttps, + Port = 12399 + }); + httpAndHttpsWithPort.Stop(); + + var httpAndHttpsFree = WireMockServer.Start(new WireMockServerSettings + { + HostingScheme = HostingScheme.HttpAndHttps + }); + httpAndHttpsFree.Stop(); + string url1 = "http://localhost:9091/"; string url2 = "http://localhost:9092/"; string url3 = "https://localhost:9443/"; diff --git a/examples/WireMock.Net.StandAlone.NETCoreApp/Properties/launchSettings.json b/examples/WireMock.Net.StandAlone.NETCoreApp/Properties/launchSettings.json index 1195cde4..8d20cf51 100644 --- a/examples/WireMock.Net.StandAlone.NETCoreApp/Properties/launchSettings.json +++ b/examples/WireMock.Net.StandAlone.NETCoreApp/Properties/launchSettings.json @@ -1,8 +1,16 @@ { - "profiles": { - "WireMock.Net.StandAlone.NETCoreApp": { - "commandName": "Project", - "commandLineArgs": "--Urls http://localhost:9091 --CorsPolicyOptions AllowAll --WireMockLogger WireMockConsoleLogger" + "profiles": { + "WireMock.Net.StandAlone.NETCoreApp": { + "commandName": "Project", + "commandLineArgs": "--Urls http://localhost:9091 --CorsPolicyOptions AllowAll --WireMockLogger WireMockConsoleLogger" + }, + "WireMock.Net.StandAlone.NETCoreAppNoPort": { + "commandName": "Project", + "commandLineArgs": "--CorsPolicyOptions AllowAll --WireMockLogger WireMockConsoleLogger" + }, + "WireMock.Net.StandAlone.NETCoreAppWithHostingProtocol": { + "commandName": "Project", + "commandLineArgs": "--HostingProtocol HttpAndHttps --CorsPolicyOptions AllowAll --WireMockLogger WireMockConsoleLogger" + } } - } } \ No newline at end of file diff --git a/src/WireMock.Net.Abstractions/Types/HostingScheme.cs b/src/WireMock.Net.Abstractions/Types/HostingScheme.cs new file mode 100644 index 00000000..7e2ffe66 --- /dev/null +++ b/src/WireMock.Net.Abstractions/Types/HostingScheme.cs @@ -0,0 +1,15 @@ +using System; + +namespace WireMock.Types; + +[Flags] +public enum HostingScheme +{ + None = 0x0, + + Http = 0x1, + + Https = 0x2, + + HttpAndHttps = Http | Https +} \ No newline at end of file diff --git a/src/WireMock.Net/Owin/HostUrlDetails.cs b/src/WireMock.Net/Owin/HostUrlDetails.cs index e988ae43..56682bff 100644 --- a/src/WireMock.Net/Owin/HostUrlDetails.cs +++ b/src/WireMock.Net/Owin/HostUrlDetails.cs @@ -1,12 +1,15 @@ namespace WireMock.Owin; +/// +/// https://en.wikipedia.org/wiki/Uniform_Resource_Identifier +/// internal struct HostUrlDetails { public bool IsHttps { get; set; } public string Url { get; set; } - public string Protocol { get; set; } + public string Scheme { get; set; } public string Host { get; set; } diff --git a/src/WireMock.Net/Owin/HostUrlOptions.cs b/src/WireMock.Net/Owin/HostUrlOptions.cs index 77dcf1e0..1e515043 100644 --- a/src/WireMock.Net/Owin/HostUrlOptions.cs +++ b/src/WireMock.Net/Owin/HostUrlOptions.cs @@ -1,34 +1,49 @@ using System.Collections.Generic; +using WireMock.Types; using WireMock.Util; namespace WireMock.Owin; internal class HostUrlOptions { - private const string LOCALHOST = "localhost"; + private const string Localhost = "localhost"; public ICollection? Urls { get; set; } public int? Port { get; set; } - public bool UseSSL { get; set; } + public int? HttpsPort { get; set; } - public ICollection GetDetails() + public HostingScheme HostingScheme { get; set; } + + public IReadOnlyList GetDetails() { var list = new List(); if (Urls == null) { - int port = Port > 0 ? Port.Value : FindFreeTcpPort(); - string protocol = UseSSL ? "https" : "http"; - list.Add(new HostUrlDetails { IsHttps = UseSSL, Url = $"{protocol}://{LOCALHOST}:{port}", Protocol = protocol, Host = LOCALHOST, Port = port }); + if (HostingScheme is HostingScheme.Http or HostingScheme.Https) + { + var port = Port > 0 ? Port.Value : FindFreeTcpPort(); + var scheme = HostingScheme == HostingScheme.Https ? "https" : "http"; + list.Add(new HostUrlDetails { IsHttps = HostingScheme == HostingScheme.Https, Url = $"{scheme}://{Localhost}:{port}", Scheme = scheme, Host = Localhost, Port = port }); + } + + if (HostingScheme == HostingScheme.HttpAndHttps) + { + var httpPort = Port > 0 ? Port.Value : FindFreeTcpPort(); + list.Add(new HostUrlDetails { IsHttps = false, Url = $"http://{Localhost}:{httpPort}", Scheme = "http", Host = Localhost, Port = httpPort }); + + var httpsPort = FindFreeTcpPort(); // In this scenario, always get a free port for https. + list.Add(new HostUrlDetails { IsHttps = true, Url = $"https://{Localhost}:{httpsPort}", Scheme = "https", Host = Localhost, Port = httpsPort }); + } } else { foreach (string url in Urls) { - if (PortUtils.TryExtract(url, out bool isHttps, out var protocol, out var host, out int port)) + if (PortUtils.TryExtract(url, out var isHttps, out var protocol, out var host, out var port)) { - list.Add(new HostUrlDetails { IsHttps = isHttps, Url = url, Protocol = protocol, Host = host, Port = port }); + list.Add(new HostUrlDetails { IsHttps = isHttps, Url = url, Scheme = protocol, Host = host, Port = port }); } } } diff --git a/src/WireMock.Net/Server/WireMockServer.cs b/src/WireMock.Net/Server/WireMockServer.cs index c44a5ff0..cb6248f9 100644 --- a/src/WireMock.Net/Server/WireMockServer.cs +++ b/src/WireMock.Net/Server/WireMockServer.cs @@ -21,6 +21,7 @@ using WireMock.RequestBuilders; using WireMock.ResponseProviders; using WireMock.Serialization; using WireMock.Settings; +using WireMock.Types; namespace WireMock.Server; @@ -48,7 +49,7 @@ public partial class WireMockServer : IWireMockServer /// [PublicAPI] - public int Port => Ports?.FirstOrDefault() ?? default(int); + public int Port => Ports?.FirstOrDefault() ?? default; /// [PublicAPI] @@ -269,11 +270,22 @@ public partial class WireMockServer : IWireMockServer } else { - urlOptions = new HostUrlOptions + if (settings.HostingScheme is not null) { - UseSSL = settings.UseSSL == true, - Port = settings.Port - }; + urlOptions = new HostUrlOptions + { + HostingScheme = settings.HostingScheme.Value, + Port = settings.Port + }; + } + else + { + urlOptions = new HostUrlOptions + { + HostingScheme = settings.UseSSL == true ? HostingScheme.Https : HostingScheme.Http, + Port = settings.Port + }; + } } _options.FileSystemHandler = _settings.FileSystemHandler; diff --git a/src/WireMock.Net/Settings/SimpleCommandLineParser.cs b/src/WireMock.Net/Settings/SimpleCommandLineParser.cs index f1c61ee9..e7be36a7 100644 --- a/src/WireMock.Net/Settings/SimpleCommandLineParser.cs +++ b/src/WireMock.Net/Settings/SimpleCommandLineParser.cs @@ -94,6 +94,26 @@ internal class SimpleCommandLineParser }, defaultValue); } + public TEnum? GetEnumValue(string name) + where TEnum : struct + { + return GetValue(name, values => + { + var value = values.FirstOrDefault(); + return Enum.TryParse(value, true, out var enumValue) ? enumValue : (TEnum?)null; + }); + } + + public TEnum GetEnumValue(string name, TEnum defaultValue) + where TEnum : struct + { + return GetValue(name, values => + { + var value = values.FirstOrDefault(); + return Enum.TryParse(value, true, out var enumValue) ? enumValue : defaultValue; + }, defaultValue); + } + public string GetStringValue(string name, string defaultValue) { return GetValue(name, values => values.FirstOrDefault() ?? defaultValue, defaultValue); diff --git a/src/WireMock.Net/Settings/WireMockServerSettings.cs b/src/WireMock.Net/Settings/WireMockServerSettings.cs index 0efa82be..ffb241ce 100644 --- a/src/WireMock.Net/Settings/WireMockServerSettings.cs +++ b/src/WireMock.Net/Settings/WireMockServerSettings.cs @@ -9,6 +9,7 @@ using WireMock.Handlers; using WireMock.Logging; using WireMock.Matchers; using WireMock.RegularExpressions; +using WireMock.Types; #if USE_ASPNETCORE using Microsoft.Extensions.DependencyInjection; using WireMock.Types; @@ -22,7 +23,7 @@ namespace WireMock.Settings public class WireMockServerSettings { /// - /// Gets or sets the port. + /// Gets or sets the http port. /// [PublicAPI] public int? Port { get; set; } @@ -34,6 +35,12 @@ namespace WireMock.Settings [PublicAPI] public bool? UseSSL { get; set; } + /// + /// Defines on which scheme (http/https) to host. (This overrides the UseSSL value). + /// + [PublicAPI] + public HostingScheme? HostingScheme { get; set; } + /// /// Gets or sets whether to start admin interface. /// diff --git a/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs b/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs index b812893a..2caca070 100644 --- a/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs +++ b/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs @@ -54,14 +54,11 @@ public static class WireMockServerSettingsParser UseRegexExtended = parser.GetBoolValue(nameof(WireMockServerSettings.UseRegexExtended), true), WatchStaticMappings = parser.GetBoolValue("WatchStaticMappings"), WatchStaticMappingsInSubdirectories = parser.GetBoolValue("WatchStaticMappingsInSubdirectories"), + HostingScheme = parser.GetEnumValue(nameof(WireMockServerSettings.HostingScheme)) }; #if USE_ASPNETCORE - settings.CorsPolicyOptions = parser.GetValue(nameof(WireMockServerSettings.CorsPolicyOptions), values => - { - var value = string.Join(string.Empty, values); - return Enum.TryParse(value, true, out var corsPolicyOptions) ? corsPolicyOptions : CorsPolicyOptions.None; - }); + settings.CorsPolicyOptions = parser.GetEnumValue(nameof(WireMockServerSettings.CorsPolicyOptions), CorsPolicyOptions.None); #endif if (logger != null) @@ -77,7 +74,7 @@ public static class WireMockServerSettingsParser { settings.Port = parser.GetIntValue(nameof(WireMockServerSettings.Port)); } - else + else if (settings.HostingScheme is null) { settings.Urls = parser.GetValues("Urls", new[] { "http://*:9091/" }); } @@ -95,7 +92,7 @@ public static class WireMockServerSettingsParser SaveMapping = parser.GetBoolValue("SaveMapping"), SaveMappingForStatusCodePattern = parser.GetStringValue("SaveMappingForStatusCodePattern", "*"), SaveMappingToFile = parser.GetBoolValue("SaveMappingToFile"), - Url = proxyUrl + Url = proxyUrl! }; string? proxyAddress = parser.GetStringValue("WebProxyAddress"); @@ -103,7 +100,7 @@ public static class WireMockServerSettingsParser { settings.ProxyAndRecordSettings.WebProxySettings = new WebProxySettings { - Address = proxyAddress, + Address = proxyAddress!, UserName = parser.GetStringValue("WebProxyUserName"), Password = parser.GetStringValue("WebProxyPassword") };