From 0f57724a0c026381a16b9e8dca5c58d3757b6a02 Mon Sep 17 00:00:00 2001 From: Bart Koelman <104792814+bart-vmware@users.noreply.github.com> Date: Thu, 14 Aug 2025 11:10:08 +0200 Subject: [PATCH 1/5] Escape comma in Config Server environment setting, to fix crash in URL masking --- .../ConfigServer/ConfigServerClientOptions.cs | 2 +- .../ConfigServerConfigurationProvider.cs | 2 +- .../src/ConfigServer/ConfigurationSchema.json | 2 +- ...igServerConfigurationProviderTest.Settings.cs | 16 ++++++++++++++++ 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/Configuration/src/ConfigServer/ConfigServerClientOptions.cs b/src/Configuration/src/ConfigServer/ConfigServerClientOptions.cs index 8a8c5f3e29..26f4183da7 100644 --- a/src/Configuration/src/ConfigServer/ConfigServerClientOptions.cs +++ b/src/Configuration/src/ConfigServer/ConfigServerClientOptions.cs @@ -32,7 +32,7 @@ public sealed class ConfigServerClientOptions : IValidateCertificatesOptions public bool FailFast { get; set; } /// - /// Gets or sets the environment used when accessing configuration data. Default value: "Production". + /// Gets or sets a comma-separated list of environments used when accessing configuration data. Default value: "Production". /// [ConfigurationKeyName("Env")] public string? Environment { get; set; } = "Production"; diff --git a/src/Configuration/src/ConfigServer/ConfigServerConfigurationProvider.cs b/src/Configuration/src/ConfigServer/ConfigServerConfigurationProvider.cs index 6c9441a596..ecd60535c6 100644 --- a/src/Configuration/src/ConfigServer/ConfigServerConfigurationProvider.cs +++ b/src/Configuration/src/ConfigServer/ConfigServerConfigurationProvider.cs @@ -638,7 +638,7 @@ internal Uri BuildConfigServerUri(string serverUri, string? label) uriBuilder.Password = ClientOptions.Password; } - string pathSuffix = $"{ClientOptions.Name}/{ClientOptions.Environment}"; + string pathSuffix = $"{ClientOptions.Name}/{WebUtility.UrlEncode(ClientOptions.Environment)}"; if (!string.IsNullOrWhiteSpace(label)) { diff --git a/src/Configuration/src/ConfigServer/ConfigurationSchema.json b/src/Configuration/src/ConfigServer/ConfigurationSchema.json index 18d867042d..8f9e677fc0 100644 --- a/src/Configuration/src/ConfigServer/ConfigurationSchema.json +++ b/src/Configuration/src/ConfigServer/ConfigurationSchema.json @@ -90,7 +90,7 @@ }, "Env": { "type": "string", - "description": "Gets or sets the environment used when accessing configuration data. Default value: \"Production\"." + "description": "Gets or sets a comma-separated list of environments used when accessing configuration data. Default value: \"Production\"." }, "FailFast": { "type": "boolean", diff --git a/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationProviderTest.Settings.cs b/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationProviderTest.Settings.cs index f595ecb8aa..05587bba20 100644 --- a/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationProviderTest.Settings.cs +++ b/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationProviderTest.Settings.cs @@ -179,4 +179,20 @@ public void GetConfigServerUri_WithEndingSlash() path.Should().Be($"http://localhost:9999/{options.Name}/{options.Environment}"); } + + [Fact] + public void GetConfigServerUri_MultipleEnvironments_EncodesComma() + { + var options = new ConfigServerClientOptions + { + Name = "myName", + Environment = "one,two", + Label = "demo" + }; + + using var provider = new ConfigServerConfigurationProvider(options, null, null, NullLoggerFactory.Instance); + string path = provider.BuildConfigServerUri(options.Uri!, options.Label).ToString(); + + path.Should().Be("http://localhost:8888/myName/one%2Ctwo/demo"); + } } From c75c67e369237ebd9a4aafcd7f65df97ca3fea90 Mon Sep 17 00:00:00 2001 From: Bart Koelman <104792814+bart-vmware@users.noreply.github.com> Date: Thu, 14 Aug 2025 11:58:04 +0200 Subject: [PATCH 2/5] Additional hardening against special characters in Config Server URL --- .../ConfigServerConfigurationProvider.cs | 8 +-- ...erverConfigurationProviderTest.Settings.cs | 51 +++++++++++++------ .../RegisterMultipleDiscoveryClientsTest.cs | 6 +-- 3 files changed, 42 insertions(+), 23 deletions(-) diff --git a/src/Configuration/src/ConfigServer/ConfigServerConfigurationProvider.cs b/src/Configuration/src/ConfigServer/ConfigServerConfigurationProvider.cs index ecd60535c6..8960c8607e 100644 --- a/src/Configuration/src/ConfigServer/ConfigServerConfigurationProvider.cs +++ b/src/Configuration/src/ConfigServer/ConfigServerConfigurationProvider.cs @@ -630,15 +630,15 @@ internal Uri BuildConfigServerUri(string serverUri, string? label) if (!string.IsNullOrEmpty(ClientOptions.Username)) { - uriBuilder.UserName = ClientOptions.Username; + uriBuilder.UserName = WebUtility.UrlEncode(ClientOptions.Username); } if (!string.IsNullOrEmpty(ClientOptions.Password)) { - uriBuilder.Password = ClientOptions.Password; + uriBuilder.Password = WebUtility.UrlEncode(ClientOptions.Password); } - string pathSuffix = $"{ClientOptions.Name}/{WebUtility.UrlEncode(ClientOptions.Environment)}"; + string pathSuffix = $"{WebUtility.UrlEncode(ClientOptions.Name)}/{WebUtility.UrlEncode(ClientOptions.Environment)}"; if (!string.IsNullOrWhiteSpace(label)) { @@ -648,7 +648,7 @@ internal Uri BuildConfigServerUri(string serverUri, string? label) label = label.Replace("/", "(_)", StringComparison.Ordinal); } - pathSuffix = $"{pathSuffix}/{label.Trim()}"; + pathSuffix = $"{pathSuffix}/{WebUtility.UrlEncode(label)}"; } if (!uriBuilder.Path.EndsWith('/')) diff --git a/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationProviderTest.Settings.cs b/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationProviderTest.Settings.cs index 05587bba20..1cd6b6ccb9 100644 --- a/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationProviderTest.Settings.cs +++ b/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationProviderTest.Settings.cs @@ -79,9 +79,9 @@ public void GetConfigServerUri_NoLabel() }; using var provider = new ConfigServerConfigurationProvider(options, null, null, NullLoggerFactory.Instance); - string path = provider.BuildConfigServerUri(options.Uri!, null).ToString(); + string uri = provider.BuildConfigServerUri(options.Uri!, null).ToString(); - path.Should().Be($"{options.Uri}/{options.Name}/{options.Environment}"); + uri.Should().Be($"{options.Uri}/{options.Name}/{options.Environment}"); } [Fact] @@ -95,9 +95,9 @@ public void GetConfigServerUri_WithLabel() }; using var provider = new ConfigServerConfigurationProvider(options, null, null, NullLoggerFactory.Instance); - string path = provider.BuildConfigServerUri(options.Uri!, options.Label).ToString(); + string uri = provider.BuildConfigServerUri(options.Uri!, options.Label).ToString(); - path.Should().Be($"{options.Uri}/{options.Name}/{options.Environment}/{options.Label}"); + uri.Should().Be($"{options.Uri}/{options.Name}/{options.Environment}/{options.Label}"); } [Fact] @@ -111,9 +111,9 @@ public void GetConfigServerUri_WithLabelContainingSlash() }; using var provider = new ConfigServerConfigurationProvider(options, null, null, NullLoggerFactory.Instance); - string path = provider.BuildConfigServerUri(options.Uri!, options.Label).ToString(); + string uri = provider.BuildConfigServerUri(options.Uri!, options.Label).ToString(); - path.Should().Be($"{options.Uri}/{options.Name}/{options.Environment}/myLabel(_)version"); + uri.Should().Be($"{options.Uri}/{options.Name}/{options.Environment}/myLabel(_)version"); } [Fact] @@ -127,9 +127,9 @@ public void GetConfigServerUri_WithExtraPathInfo() }; using var provider = new ConfigServerConfigurationProvider(options, null, null, NullLoggerFactory.Instance); - string path = provider.BuildConfigServerUri(options.Uri, null).ToString(); + string uri = provider.BuildConfigServerUri(options.Uri, null).ToString(); - path.Should().Be($"http://localhost:9999/myPath/path/{options.Name}/{options.Environment}"); + uri.Should().Be($"http://localhost:9999/myPath/path/{options.Name}/{options.Environment}"); } [Fact] @@ -143,9 +143,9 @@ public void GetConfigServerUri_WithExtraPathInfo_NoEndingSlash() }; using var provider = new ConfigServerConfigurationProvider(options, null, null, NullLoggerFactory.Instance); - string path = provider.BuildConfigServerUri(options.Uri, null).ToString(); + string uri = provider.BuildConfigServerUri(options.Uri, null).ToString(); - path.Should().Be($"http://localhost:9999/myPath/path/{options.Name}/{options.Environment}"); + uri.Should().Be($"http://localhost:9999/myPath/path/{options.Name}/{options.Environment}"); } [Fact] @@ -159,9 +159,9 @@ public void GetConfigServerUri_NoEndingSlash() }; using var provider = new ConfigServerConfigurationProvider(options, null, null, NullLoggerFactory.Instance); - string path = provider.BuildConfigServerUri(options.Uri, null).ToString(); + string uri = provider.BuildConfigServerUri(options.Uri, null).ToString(); - path.Should().Be($"http://localhost:9999/{options.Name}/{options.Environment}"); + uri.Should().Be($"http://localhost:9999/{options.Name}/{options.Environment}"); } [Fact] @@ -175,9 +175,9 @@ public void GetConfigServerUri_WithEndingSlash() }; using var provider = new ConfigServerConfigurationProvider(options, null, null, NullLoggerFactory.Instance); - string path = provider.BuildConfigServerUri(options.Uri, null).ToString(); + string uri = provider.BuildConfigServerUri(options.Uri, null).ToString(); - path.Should().Be($"http://localhost:9999/{options.Name}/{options.Environment}"); + uri.Should().Be($"http://localhost:9999/{options.Name}/{options.Environment}"); } [Fact] @@ -191,8 +191,27 @@ public void GetConfigServerUri_MultipleEnvironments_EncodesComma() }; using var provider = new ConfigServerConfigurationProvider(options, null, null, NullLoggerFactory.Instance); - string path = provider.BuildConfigServerUri(options.Uri!, options.Label).ToString(); + string uri = provider.BuildConfigServerUri(options.Uri!, options.Label).ToString(); - path.Should().Be("http://localhost:8888/myName/one%2Ctwo/demo"); + uri.Should().Be("http://localhost:8888/myName/one%2Ctwo/demo"); + } + + [Fact] + public void GetConfigServerUri_EncodesSpecialCharacters() + { + var options = new ConfigServerClientOptions + { + Name = "my$<>:,\"'Name", + Environment = "@/&?:\"'", + Label = "^&$@#*<>+", + Username = "a\":?'*,b/\\&", + Password = "#&:$<>'/so,\"me" + }; + + using var provider = new ConfigServerConfigurationProvider(options, null, null, NullLoggerFactory.Instance); + string uri = provider.BuildConfigServerUri(options.Uri!, options.Label).ToString(); + + uri.Should().Be( + "http://a%22%3A%3F%27*%2Cb%2F%5C%26:%23%26%3A%24%3C%3E%27%2Fso%2C%22me@localhost:8888/my%24<>%3A%2C\"%27Name/%40%2F%26%3F%3A\"%27/^%26%24%40%23*<>%2B"); } } diff --git a/src/Discovery/test/HttpClients.Test/RegisterMultipleDiscoveryClientsTest.cs b/src/Discovery/test/HttpClients.Test/RegisterMultipleDiscoveryClientsTest.cs index 5be363b7c8..98d8f09d57 100644 --- a/src/Discovery/test/HttpClients.Test/RegisterMultipleDiscoveryClientsTest.cs +++ b/src/Discovery/test/HttpClients.Test/RegisterMultipleDiscoveryClientsTest.cs @@ -602,8 +602,8 @@ public async Task EurekaWithUsernamePasswordInURL_SendsWithAuthHeader() } """; - string username = WebUtility.UrlEncode("u$er?N@me"); - string password = WebUtility.UrlEncode(":p@ssw0rd="); + string username = WebUtility.UrlEncode("u$er?,N@me"); + string password = WebUtility.UrlEncode(":p@ss,w0rd="); var appSettings = new Dictionary { @@ -619,7 +619,7 @@ public async Task EurekaWithUsernamePasswordInURL_SendsWithAuthHeader() var handler = new DelegateToMockHttpClientHandler(); - handler.Mock.Expect(HttpMethod.Get, "https://api.eureka-server.com/eureka/apps").WithHeaders("Authorization", "Basic dSRlcj9OQG1lOjpwQHNzdzByZD0=") + handler.Mock.Expect(HttpMethod.Get, "https://api.eureka-server.com/eureka/apps").WithHeaders("Authorization", "Basic dSRlcj8sTkBtZTo6cEBzcyx3MHJkPQ==") .WithHeaders("X-Discovery-AllowRedirect", "false").Respond("application/json", applicationsResponse); await using WebApplication webApplication = builder.Build(); From 33882da8705499ba798fd1056acd0f1c77082062 Mon Sep 17 00:00:00 2001 From: Bart Koelman <104792814+bart-vmware@users.noreply.github.com> Date: Thu, 14 Aug 2025 12:33:32 +0200 Subject: [PATCH 3/5] Fixed: allow colon to appear in password --- src/Common/src/Common/Extensions/UriExtensions.cs | 2 +- .../test/Common.Test/Extensions/UriExtensionsTest.cs | 12 ++++++++++++ .../src/RandomValue/RandomValueProvider.cs | 4 ++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Common/src/Common/Extensions/UriExtensions.cs b/src/Common/src/Common/Extensions/UriExtensions.cs index cbcf5f95fe..e1c400459b 100644 --- a/src/Common/src/Common/Extensions/UriExtensions.cs +++ b/src/Common/src/Common/Extensions/UriExtensions.cs @@ -48,7 +48,7 @@ public static bool TryGetUsernamePassword(this Uri uri, [NotNullWhen(true)] out string userInfo = uri.GetComponents(UriComponents.UserInfo, UriFormat.UriEscaped); - string[] parts = userInfo.Split(':'); + string[] parts = userInfo.Split(':', 2); if (parts.Length == 2) { diff --git a/src/Common/test/Common.Test/Extensions/UriExtensionsTest.cs b/src/Common/test/Common.Test/Extensions/UriExtensionsTest.cs index 1372c85db6..fbfa1a9062 100644 --- a/src/Common/test/Common.Test/Extensions/UriExtensionsTest.cs +++ b/src/Common/test/Common.Test/Extensions/UriExtensionsTest.cs @@ -40,4 +40,16 @@ public void DoNotMaskIfNoBasicAuthentication() masked.Should().Be(expected); } + + [Fact] + public void TryGetUsernamePassword_AllowsColonInPassword() + { + var uri = new Uri("http://username:pass::word@host.com"); + + bool result = uri.TryGetUsernamePassword(out string? username, out string? password); + + result.Should().BeTrue(); + username.Should().Be("username"); + password.Should().Be("pass::word"); + } } diff --git a/src/Configuration/src/RandomValue/RandomValueProvider.cs b/src/Configuration/src/RandomValue/RandomValueProvider.cs index 6546a13633..8eed55e1c4 100644 --- a/src/Configuration/src/RandomValue/RandomValueProvider.cs +++ b/src/Configuration/src/RandomValue/RandomValueProvider.cs @@ -165,7 +165,7 @@ private long GetLong() private int GetNextIntInRange(string range) { - string[] tokens = range.Split(','); + string[] tokens = range.Split(',', 2); int.TryParse(tokens[0], CultureInfo.InvariantCulture, out int start); if (tokens.Length == 1) @@ -179,7 +179,7 @@ private int GetNextIntInRange(string range) private long GetNextLongInRange(string range) { - string[] tokens = range.Split(','); + string[] tokens = range.Split(',', 2); long.TryParse(tokens[0], CultureInfo.InvariantCulture, out long start); if (tokens.Length == 1) From 695de5f3d8697c25a19a34eb320c7fcfb5d214ef Mon Sep 17 00:00:00 2001 From: Bart Koelman <104792814+bart-vmware@users.noreply.github.com> Date: Fri, 15 Aug 2025 17:07:32 +0200 Subject: [PATCH 4/5] Throw early when Config Server URL(s) is/are invalid --- .../ConfigServer/ConfigServerClientOptions.cs | 14 ++++++- .../ConfigServerConfigurationProvider.cs | 24 +++++------ ...rConfigurationBuilderExtensionsCoreTest.cs | 2 +- ...erverConfigurationBuilderExtensionsTest.cs | 2 +- ...ServerConfigurationProviderTest.Loading.cs | 41 +++++++++++++------ ...erverConfigurationProviderTest.Settings.cs | 18 ++++---- .../ConfigServerConfigurationProviderTest.cs | 8 ++-- 7 files changed, 67 insertions(+), 42 deletions(-) diff --git a/src/Configuration/src/ConfigServer/ConfigServerClientOptions.cs b/src/Configuration/src/ConfigServer/ConfigServerClientOptions.cs index 26f4183da7..d9ce59f505 100644 --- a/src/Configuration/src/ConfigServer/ConfigServerClientOptions.cs +++ b/src/Configuration/src/ConfigServer/ConfigServerClientOptions.cs @@ -143,8 +143,18 @@ public bool ValidateCertificatesAlt /// public IDictionary Headers { get; } = new Dictionary(); - internal IList GetUris() + internal List GetUris() { - return !string.IsNullOrEmpty(Uri) ? Uri.Split(CommaDelimiter, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) : []; + return Uri == null ? [] : Uri.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).Select(ParseSingleUri).ToList(); + } + + private static Uri ParseSingleUri(string uri) + { + if (!System.Uri.TryCreate(uri, UriKind.Absolute, out Uri? result)) + { + throw new ConfigServerException("One or more Config Server URIs in configuration are invalid."); + } + + return result; } } diff --git a/src/Configuration/src/ConfigServer/ConfigServerConfigurationProvider.cs b/src/Configuration/src/ConfigServer/ConfigServerConfigurationProvider.cs index 8960c8607e..98be9996da 100644 --- a/src/Configuration/src/ConfigServer/ConfigServerConfigurationProvider.cs +++ b/src/Configuration/src/ConfigServer/ConfigServerConfigurationProvider.cs @@ -223,8 +223,6 @@ public override void Load() // Adds client settings (e.g. spring:cloud:config:uri, etc.) to the Data dictionary AddConfigServerClientOptions(); - string logUri = string.Join(',', ClientOptions.GetUris().Select(uri => new Uri(uri).ToMaskedString())); - if (ClientOptions is { Retry.Enabled: true, FailFast: true }) { int attempts = 0; @@ -232,7 +230,7 @@ public override void Load() do { - _logger.LogDebug("Fetching configuration from server at: {Uri}", logUri); + _logger.LogDebug("Fetching configuration from server(s)."); try { @@ -240,7 +238,7 @@ public override void Load() } catch (ConfigServerException exception) { - _logger.LogWarning(exception, "Failed fetching configuration from server at: {Uri}.", logUri); + _logger.LogWarning(exception, "Failed fetching configuration from server(s)."); attempts++; if (attempts < ClientOptions.Retry.MaxAttempts) @@ -258,7 +256,7 @@ public override void Load() while (true); } - _logger.LogDebug("Fetching configuration from server at: {Uri}", logUri); + _logger.LogDebug("Fetching configuration from server(s)."); return await DoLoadAsync(updateDictionary, cancellationToken); } @@ -266,8 +264,8 @@ public override void Load() { Exception? error = null; - // Get arrays of Config Server uris to check - IList uris = ClientOptions.GetUris(); + // Get list of Config Server uris to check + List uris = ClientOptions.GetUris(); try { @@ -543,7 +541,7 @@ private void AddConfigServerClientOptions(Dictionary data) } } - internal async Task RemoteLoadAsync(IEnumerable requestUris, string? label, CancellationToken cancellationToken) + internal async Task RemoteLoadAsync(List requestUris, string? label, CancellationToken cancellationToken) { _logger.LogTrace("Entered {Method}", nameof(RemoteLoadAsync)); @@ -552,11 +550,13 @@ private void AddConfigServerClientOptions(Dictionary data) Exception? error = null; - foreach (string requestUri in requestUris) + foreach (Uri requestUri in requestUris) { // Make Config Server URI from settings Uri uri = BuildConfigServerUri(requestUri, label); + _logger.LogDebug("Trying to connect to Config Server at {RequestUri}", uri.ToMaskedString()); + // Get the request message _logger.LogTrace("Building HTTP request message"); HttpRequestMessage request = await GetRequestMessageAsync(uri, cancellationToken); @@ -622,11 +622,11 @@ private void AddConfigServerClientOptions(Dictionary data) /// /// The request URI for the Configuration Server. /// - internal Uri BuildConfigServerUri(string serverUri, string? label) + internal Uri BuildConfigServerUri(Uri serverUri, string? label) { - ArgumentException.ThrowIfNullOrWhiteSpace(serverUri); + ArgumentNullException.ThrowIfNull(serverUri); - var uriBuilder = new UriBuilder(new Uri(serverUri)); + var uriBuilder = new UriBuilder(serverUri); if (!string.IsNullOrEmpty(ClientOptions.Username)) { diff --git a/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationBuilderExtensionsCoreTest.cs b/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationBuilderExtensionsCoreTest.cs index 006a4608d5..9f3aa6f962 100644 --- a/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationBuilderExtensionsCoreTest.cs +++ b/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationBuilderExtensionsCoreTest.cs @@ -44,7 +44,7 @@ public void AddConfigServer_WithLoggerFactorySucceeds() IList logMessages = loggerProvider.GetAll(); logMessages.Should().Contain( - "DBUG Steeltoe.Configuration.ConfigServer.ConfigServerConfigurationProvider: Fetching configuration from server at: http://localhost:8888/"); + "DBUG Steeltoe.Configuration.ConfigServer.ConfigServerConfigurationProvider: Fetching configuration from server(s)."); } [Fact] diff --git a/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationBuilderExtensionsTest.cs b/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationBuilderExtensionsTest.cs index 64a5630dd7..af3df14ae4 100644 --- a/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationBuilderExtensionsTest.cs +++ b/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationBuilderExtensionsTest.cs @@ -185,7 +185,7 @@ public void AddConfigServer_WithLoggerFactorySucceeds() IList logMessages = loggerProvider.GetAll(); logMessages.Should().Contain( - "DBUG Steeltoe.Configuration.ConfigServer.ConfigServerConfigurationProvider: Fetching configuration from server at: http://localhost:8888/"); + "DBUG Steeltoe.Configuration.ConfigServer.ConfigServerConfigurationProvider: Fetching configuration from server(s)."); } [Theory] diff --git a/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationProviderTest.Loading.cs b/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationProviderTest.Loading.cs index a293e85687..e5f9f669ac 100644 --- a/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationProviderTest.Loading.cs +++ b/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationProviderTest.Loading.cs @@ -13,18 +13,6 @@ namespace Steeltoe.Configuration.ConfigServer.Test; public sealed partial class ConfigServerConfigurationProviderTest { - [Fact] - public async Task RemoteLoadAsync_InvalidUri() - { - var options = new ConfigServerClientOptions(); - using var provider = new ConfigServerConfigurationProvider(options, null, null, NullLoggerFactory.Instance); - - // ReSharper disable once AccessToDisposedClosure - Func action = async () => await provider.RemoteLoadAsync([@"foobar\foobar\"], null, TestContext.Current.CancellationToken); - - await action.Should().ThrowExactlyAsync(); - } - [Fact] public async Task RemoteLoadAsync_HostTimesOut() { @@ -35,9 +23,10 @@ public async Task RemoteLoadAsync_HostTimesOut() var httpClientHandler = new SlowHttpClientHandler(1.Seconds(), new HttpResponseMessage()); using var provider = new ConfigServerConfigurationProvider(options, null, httpClientHandler, NullLoggerFactory.Instance); + List requestUris = [new("http://localhost:9999/app/profile")]; // ReSharper disable once AccessToDisposedClosure - Func action = async () => await provider.RemoteLoadAsync(["http://localhost:9999/app/profile"], null, TestContext.Current.CancellationToken); + Func action = async () => await provider.RemoteLoadAsync(requestUris, null, TestContext.Current.CancellationToken); (await action.Should().ThrowExactlyAsync()).WithInnerExceptionExactly(); } @@ -506,6 +495,32 @@ public async Task Load_MultipleConfigServers_ReturnsNotFoundStatus__DoesNotConti startup.RequestCount.Should().Be(1); } + [Fact] + public async Task Load_UriInvalid_FailFastEnabled() + { + using var startup = new TestConfigServerStartup(); + startup.ReturnStatus = [500]; + + await using WebApplication app = TestWebApplicationBuilderFactory.Create().Build(); + startup.Configure(app); + await app.StartAsync(TestContext.Current.CancellationToken); + + using TestServer server = app.GetTestServer(); + server.BaseAddress = new Uri("http://localhost:8888"); + + ConfigServerClientOptions options = GetCommonOptions(); + options.Uri = "http://username:p@ssword@localhost:8888"; + options.FailFast = true; + + using var httpClientHandler = new ForwardingHttpClientHandler(server.CreateHandler()); + using var provider = new ConfigServerConfigurationProvider(options, null, httpClientHandler, NullLoggerFactory.Instance); + + // ReSharper disable once AccessToDisposedClosure + Func action = async () => await provider.LoadInternalAsync(true, TestContext.Current.CancellationToken); + + await action.Should().ThrowExactlyAsync().WithMessage("One or more Config Server URIs in configuration are invalid."); + } + [Fact] public async Task Load_ConfigServerReturnsBadStatus_FailFastEnabled() { diff --git a/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationProviderTest.Settings.cs b/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationProviderTest.Settings.cs index 1cd6b6ccb9..8380debc6b 100644 --- a/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationProviderTest.Settings.cs +++ b/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationProviderTest.Settings.cs @@ -79,7 +79,7 @@ public void GetConfigServerUri_NoLabel() }; using var provider = new ConfigServerConfigurationProvider(options, null, null, NullLoggerFactory.Instance); - string uri = provider.BuildConfigServerUri(options.Uri!, null).ToString(); + string uri = provider.BuildConfigServerUri(new Uri(options.Uri!), null).ToString(); uri.Should().Be($"{options.Uri}/{options.Name}/{options.Environment}"); } @@ -95,7 +95,7 @@ public void GetConfigServerUri_WithLabel() }; using var provider = new ConfigServerConfigurationProvider(options, null, null, NullLoggerFactory.Instance); - string uri = provider.BuildConfigServerUri(options.Uri!, options.Label).ToString(); + string uri = provider.BuildConfigServerUri(new Uri(options.Uri!), options.Label).ToString(); uri.Should().Be($"{options.Uri}/{options.Name}/{options.Environment}/{options.Label}"); } @@ -111,7 +111,7 @@ public void GetConfigServerUri_WithLabelContainingSlash() }; using var provider = new ConfigServerConfigurationProvider(options, null, null, NullLoggerFactory.Instance); - string uri = provider.BuildConfigServerUri(options.Uri!, options.Label).ToString(); + string uri = provider.BuildConfigServerUri(new Uri(options.Uri!), options.Label).ToString(); uri.Should().Be($"{options.Uri}/{options.Name}/{options.Environment}/myLabel(_)version"); } @@ -127,7 +127,7 @@ public void GetConfigServerUri_WithExtraPathInfo() }; using var provider = new ConfigServerConfigurationProvider(options, null, null, NullLoggerFactory.Instance); - string uri = provider.BuildConfigServerUri(options.Uri, null).ToString(); + string uri = provider.BuildConfigServerUri(new Uri(options.Uri), null).ToString(); uri.Should().Be($"http://localhost:9999/myPath/path/{options.Name}/{options.Environment}"); } @@ -143,7 +143,7 @@ public void GetConfigServerUri_WithExtraPathInfo_NoEndingSlash() }; using var provider = new ConfigServerConfigurationProvider(options, null, null, NullLoggerFactory.Instance); - string uri = provider.BuildConfigServerUri(options.Uri, null).ToString(); + string uri = provider.BuildConfigServerUri(new Uri(options.Uri), null).ToString(); uri.Should().Be($"http://localhost:9999/myPath/path/{options.Name}/{options.Environment}"); } @@ -159,7 +159,7 @@ public void GetConfigServerUri_NoEndingSlash() }; using var provider = new ConfigServerConfigurationProvider(options, null, null, NullLoggerFactory.Instance); - string uri = provider.BuildConfigServerUri(options.Uri, null).ToString(); + string uri = provider.BuildConfigServerUri(new Uri(options.Uri), null).ToString(); uri.Should().Be($"http://localhost:9999/{options.Name}/{options.Environment}"); } @@ -175,7 +175,7 @@ public void GetConfigServerUri_WithEndingSlash() }; using var provider = new ConfigServerConfigurationProvider(options, null, null, NullLoggerFactory.Instance); - string uri = provider.BuildConfigServerUri(options.Uri, null).ToString(); + string uri = provider.BuildConfigServerUri(new Uri(options.Uri), null).ToString(); uri.Should().Be($"http://localhost:9999/{options.Name}/{options.Environment}"); } @@ -191,7 +191,7 @@ public void GetConfigServerUri_MultipleEnvironments_EncodesComma() }; using var provider = new ConfigServerConfigurationProvider(options, null, null, NullLoggerFactory.Instance); - string uri = provider.BuildConfigServerUri(options.Uri!, options.Label).ToString(); + string uri = provider.BuildConfigServerUri(new Uri(options.Uri!), options.Label).ToString(); uri.Should().Be("http://localhost:8888/myName/one%2Ctwo/demo"); } @@ -209,7 +209,7 @@ public void GetConfigServerUri_EncodesSpecialCharacters() }; using var provider = new ConfigServerConfigurationProvider(options, null, null, NullLoggerFactory.Instance); - string uri = provider.BuildConfigServerUri(options.Uri!, options.Label).ToString(); + string uri = provider.BuildConfigServerUri(new Uri(options.Uri!), options.Label).ToString(); uri.Should().Be( "http://a%22%3A%3F%27*%2Cb%2F%5C%26:%23%26%3A%24%3C%3E%27%2Fso%2C%22me@localhost:8888/my%24<>%3A%2C\"%27Name/%40%2F%26%3F%3A\"%27/^%26%24%40%23*<>%2B"); diff --git a/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationProviderTest.cs b/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationProviderTest.cs index a25a8219e9..6638ca8c82 100644 --- a/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationProviderTest.cs +++ b/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationProviderTest.cs @@ -145,7 +145,7 @@ public async Task GetRequestMessage_AddsBasicAuthIfUserNameAndPasswordInURL() using var provider = new ConfigServerConfigurationProvider(options, null, null, NullLoggerFactory.Instance); - Uri requestUri = provider.BuildConfigServerUri(options.Uri, null); + Uri requestUri = provider.BuildConfigServerUri(new Uri(options.Uri), null); HttpRequestMessage request = await provider.GetRequestMessageAsync(requestUri, TestContext.Current.CancellationToken); request.Method.Should().Be(HttpMethod.Get); @@ -169,7 +169,7 @@ public async Task GetRequestMessage_AddsBasicAuthIfUserNameAndPasswordInSettings using var provider = new ConfigServerConfigurationProvider(options, null, null, NullLoggerFactory.Instance); - Uri requestUri = provider.BuildConfigServerUri(options.Uri, null); + Uri requestUri = provider.BuildConfigServerUri(new Uri(options.Uri), null); HttpRequestMessage request = await provider.GetRequestMessageAsync(requestUri, TestContext.Current.CancellationToken); request.Method.Should().Be(HttpMethod.Get); @@ -193,7 +193,7 @@ public async Task GetRequestMessage_BasicAuthInSettingsOverridesUserNameAndPassw using var provider = new ConfigServerConfigurationProvider(options, null, null, NullLoggerFactory.Instance); - Uri requestUri = provider.BuildConfigServerUri(options.Uri, null); + Uri requestUri = provider.BuildConfigServerUri(new Uri(options.Uri), null); HttpRequestMessage request = await provider.GetRequestMessageAsync(requestUri, TestContext.Current.CancellationToken); request.Method.Should().Be(HttpMethod.Get); @@ -215,7 +215,7 @@ public async Task GetRequestMessage_AddsVaultToken_IfNeeded() using var provider = new ConfigServerConfigurationProvider(options, null, null, NullLoggerFactory.Instance); - Uri requestUri = provider.BuildConfigServerUri(options.Uri!, null); + Uri requestUri = provider.BuildConfigServerUri(new Uri(options.Uri!), null); HttpRequestMessage request = await provider.GetRequestMessageAsync(requestUri, TestContext.Current.CancellationToken); request.Method.Should().Be(HttpMethod.Get); From b9edbb31f10acdced125d010a8c006c3add62d7c Mon Sep 17 00:00:00 2001 From: Bart Koelman <104792814+bart-vmware@users.noreply.github.com> Date: Fri, 15 Aug 2025 17:22:53 +0200 Subject: [PATCH 5/5] Code cleanup --- .../ConfigServerConfigurationBuilderExtensionsCoreTest.cs | 3 +-- .../ConfigServerConfigurationBuilderExtensionsTest.cs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationBuilderExtensionsCoreTest.cs b/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationBuilderExtensionsCoreTest.cs index 9f3aa6f962..b78ec586fa 100644 --- a/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationBuilderExtensionsCoreTest.cs +++ b/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationBuilderExtensionsCoreTest.cs @@ -43,8 +43,7 @@ public void AddConfigServer_WithLoggerFactorySucceeds() IList logMessages = loggerProvider.GetAll(); - logMessages.Should().Contain( - "DBUG Steeltoe.Configuration.ConfigServer.ConfigServerConfigurationProvider: Fetching configuration from server(s)."); + logMessages.Should().Contain("DBUG Steeltoe.Configuration.ConfigServer.ConfigServerConfigurationProvider: Fetching configuration from server(s)."); } [Fact] diff --git a/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationBuilderExtensionsTest.cs b/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationBuilderExtensionsTest.cs index af3df14ae4..57ed14e112 100644 --- a/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationBuilderExtensionsTest.cs +++ b/src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationBuilderExtensionsTest.cs @@ -184,8 +184,7 @@ public void AddConfigServer_WithLoggerFactorySucceeds() IList logMessages = loggerProvider.GetAll(); - logMessages.Should().Contain( - "DBUG Steeltoe.Configuration.ConfigServer.ConfigServerConfigurationProvider: Fetching configuration from server(s)."); + logMessages.Should().Contain("DBUG Steeltoe.Configuration.ConfigServer.ConfigServerConfigurationProvider: Fetching configuration from server(s)."); } [Theory]