From 13287596a6b2f2aef2a8b992e12d3a382317cc93 Mon Sep 17 00:00:00 2001 From: Mahdi Golestan Date: Mon, 6 Oct 2025 21:44:49 +0330 Subject: [PATCH 1/3] Improve server creation and URL handling logic Updated the `MakeServers` method to include checks for `defaultUrl` and refined host validation. Modified the `BuildUrl` method to correctly extract ports and remove default ports from URLs. Added unit tests in `OpenApiServerTests.cs` to ensure proper handling of base URLs and ports across various scenarios. --- .../Reader/V2/OpenApiDocumentDeserializer.cs | 21 ++- .../V2Tests/OpenApiServerTests.cs | 172 ++++++++++++++++++ 2 files changed, 186 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.OpenApi/Reader/V2/OpenApiDocumentDeserializer.cs b/src/Microsoft.OpenApi/Reader/V2/OpenApiDocumentDeserializer.cs index fc981bf7e..5ee8a85ec 100644 --- a/src/Microsoft.OpenApi/Reader/V2/OpenApiDocumentDeserializer.cs +++ b/src/Microsoft.OpenApi/Reader/V2/OpenApiDocumentDeserializer.cs @@ -124,14 +124,14 @@ private static void MakeServers(IList servers, ParsingContext con basePath = "/"; } - // If nothing is provided, don't create a server - if (host == null && basePath == null && schemes == null) + // If nothing is provided and there's no defaultUrl, don't create a server + if (string.IsNullOrEmpty(host) && string.IsNullOrEmpty(basePath) && (schemes == null || schemes.Count == 0) && defaultUrl == null) { return; } //Validate host - if (host != null && !IsHostValid(host)) + if (!string.IsNullOrEmpty(host) && !IsHostValid(host!)) { rootNode.Context.Diagnostic.Errors.Add(new(rootNode.Context.GetLocation(), "Invalid host")); return; @@ -140,7 +140,7 @@ private static void MakeServers(IList servers, ParsingContext con // Fill in missing information based on the defaultUrl if (defaultUrl != null) { - host = host ?? defaultUrl.GetComponents(UriComponents.NormalizedHost, UriFormat.SafeUnescaped); + host = host ?? defaultUrl.GetComponents(UriComponents.Host | UriComponents.Port, UriFormat.SafeUnescaped); basePath = basePath ?? defaultUrl.GetComponents(UriComponents.Path, UriFormat.SafeUnescaped); schemes = schemes ?? [defaultUrl.GetComponents(UriComponents.Scheme, UriFormat.SafeUnescaped)]; } @@ -150,7 +150,7 @@ private static void MakeServers(IList servers, ParsingContext con } // Create the Server objects - if (schemes is {Count: > 0}) + if (schemes is { Count: > 0 }) { foreach (var scheme in schemes) { @@ -202,8 +202,8 @@ private static string BuildUrl(string? scheme, string? host, string? basePath) if (pieces is not null) { host = pieces[0]; - port = int.Parse(pieces[pieces.Count() -1], CultureInfo.InvariantCulture); - } + port = int.Parse(pieces[pieces.Count() - 1], CultureInfo.InvariantCulture); + } } var uriBuilder = new UriBuilder @@ -218,6 +218,13 @@ private static string BuildUrl(string? scheme, string? host, string? basePath) uriBuilder.Port = port.Value; } + // Remove default ports to clean up the URL + if (("https".Equals(uriBuilder.Scheme, StringComparison.OrdinalIgnoreCase) && uriBuilder.Port == 443) || + ("http".Equals(uriBuilder.Scheme, StringComparison.OrdinalIgnoreCase) && uriBuilder.Port == 80)) + { + uriBuilder.Port = -1; // Setting to -1 removes the port from the URL + } + return uriBuilder.ToString(); } diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiServerTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiServerTests.cs index f5f6078a2..c2a8b7115 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiServerTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiServerTests.cs @@ -318,5 +318,177 @@ public void InvalidHostShouldYieldError() Format = OpenApiConstants.Yaml }, result.Diagnostic); } + + [Fact] + public void BaseUrlWithPortShouldPreservePort() + { + var input = + """ + swagger: 2.0 + info: + title: test + version: 1.0.0 + paths: {} + """; + var settings = new OpenApiReaderSettings + { + BaseUrl = new("http://demo.testfire.net:8080") + }; + settings.AddYamlReader(); + + var result = OpenApiDocument.Parse(input, "yaml", settings); + + var server = result.Document.Servers.First(); + Assert.Single(result.Document.Servers); + Assert.Equal("http://demo.testfire.net:8080", server.Url); + } + + [Fact] + public void BaseUrlWithPortAndPathShouldPreservePort() + { + var input = + """ + swagger: 2.0 + info: + title: test + version: 1.0.0 + paths: {} + """; + var settings = new OpenApiReaderSettings + { + BaseUrl = new("http://demo.testfire.net:8080/swagger/properties.json") + }; + settings.AddYamlReader(); + + var result = OpenApiDocument.Parse(input, "yaml", settings); + + var server = result.Document.Servers.First(); + Assert.Single(result.Document.Servers); + Assert.Equal("http://demo.testfire.net:8080/swagger/properties.json", server.Url); + } + + [Fact] + public void BaseUrlWithNonStandardPortShouldPreservePort() + { + var input = + """ + swagger: 2.0 + info: + title: test + version: 1.0.0 + paths: {} + """; + var settings = new OpenApiReaderSettings + { + BaseUrl = new("https://api.example.com:9443/v1/openapi.yaml") + }; + settings.AddYamlReader(); + + var result = OpenApiDocument.Parse(input, "yaml", settings); + + var server = result.Document.Servers.First(); + Assert.Single(result.Document.Servers); + Assert.Equal("https://api.example.com:9443/v1/openapi.yaml", server.Url); + } + + [Fact] + public void BaseUrlWithStandardHttpsPortShouldRemovePort() + { + var input = + """ + swagger: 2.0 + info: + title: test + version: 1.0.0 + paths: {} + """; + var settings = new OpenApiReaderSettings + { + BaseUrl = new("https://foo.bar:443/api") + }; + settings.AddYamlReader(); + + var result = OpenApiDocument.Parse(input, "yaml", settings); + + var server = result.Document.Servers.First(); + Assert.Single(result.Document.Servers); + Assert.Equal("https://foo.bar/api", server.Url); + } + + [Fact] + public void BaseUrlWithStandardHttpPortShouldRemovePort() + { + var input = + """ + swagger: 2.0 + info: + title: test + version: 1.0.0 + paths: {} + """; + var settings = new OpenApiReaderSettings + { + BaseUrl = new("http://foo.bar:80/api") + }; + settings.AddYamlReader(); + + var result = OpenApiDocument.Parse(input, "yaml", settings); + + var server = result.Document.Servers.First(); + Assert.Single(result.Document.Servers); + Assert.Equal("http://foo.bar/api", server.Url); + } + + [Fact] + public void HostWithStandardHttpsPortShouldRemovePort() + { + var input = + """ + swagger: 2.0 + info: + title: test + version: 1.0.0 + host: foo.bar:443 + schemes: + - https + paths: {} + """; + var settings = new OpenApiReaderSettings + { + }; + settings.AddYamlReader(); + + var result = OpenApiDocument.Parse(input, "yaml", settings); + + var server = result.Document.Servers.First(); + Assert.Single(result.Document.Servers); + Assert.Equal("https://foo.bar", server.Url); + } + + [Fact] + public void HostWithStandardHttpPortShouldRemovePort() + { + var input = + """ + swagger: 2.0 + info: + title: test + version: 1.0.0 + host: foo.bar:80 + schemes: + - http + paths: {} + """; + var settings = new OpenApiReaderSettings + { + }; + settings.AddYamlReader(); + + var result = OpenApiDocument.Parse(input, "yaml", settings); + + var server = result.Document.Servers.First(); + Assert.Single(result.Document.Servers); + Assert.Equal("http://foo.bar", server.Url); + } } } From 929fcaa12cd32db24485671b41bc639564a07fd1 Mon Sep 17 00:00:00 2001 From: "release-please-token-provider[bot]" <225477224+release-please-token-provider[bot]@users.noreply.github.com> Date: Mon, 6 Oct 2025 18:35:58 +0000 Subject: [PATCH 2/3] chore(main): release 2.3.4 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ Directory.Build.props | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index a3976f1a8..8a45bd5dd 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.3.3" + ".": "2.3.4" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ca0751e8..361168645 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## [2.3.4](https://github.com/microsoft/OpenAPI.NET/compare/v2.3.3...v2.3.4) (2025-10-06) + + +### Bug Fixes + +* Improve server creation and URL handling logic to maintain port ([3e6ee80](https://github.com/microsoft/OpenAPI.NET/commit/3e6ee80491ab32d810d40f29761b8eb8e4c4ce23)) +* missing deserialization for header content property in 3.1 and 3.0 ([717f154](https://github.com/microsoft/OpenAPI.NET/commit/717f1547bf94c3e9f76078d9a3b93ce684e17306)) +* missing deserialization for header content property in 3.1 and 3.0 ([0fdfae1](https://github.com/microsoft/OpenAPI.NET/commit/0fdfae1b0bf4d371af8ad3bfa6ad4df3da8d545b)) + ## [2.3.3](https://github.com/microsoft/OpenAPI.NET/compare/v2.3.2...v2.3.3) (2025-10-02) diff --git a/Directory.Build.props b/Directory.Build.props index d61db6d94..c8f5a4612 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -12,7 +12,7 @@ https://github.com/Microsoft/OpenAPI.NET © Microsoft Corporation. All rights reserved. OpenAPI .NET - 2.3.3 + 2.3.4 From 3d3269ad08b31ae2437db7214b21d4d1c67f03cd Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Mon, 6 Oct 2025 14:52:14 -0400 Subject: [PATCH 3/3] ci: fixes target branch definition to support multiple releases automatically Signed-off-by: Vincent Biret --- .github/workflows/release-please-gha.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release-please-gha.yml b/.github/workflows/release-please-gha.yml index a730548a2..0fe9326ea 100644 --- a/.github/workflows/release-please-gha.yml +++ b/.github/workflows/release-please-gha.yml @@ -35,3 +35,4 @@ jobs: token: ${{ steps.app-token.outputs.token }} config-file: release-please-config.json manifest-file: .release-please-manifest.json + target-branch: ${{ github.ref_name }}