From b06c4fb8ee9fa4591383b5379078c2c24c7b0d17 Mon Sep 17 00:00:00 2001 From: Scott Ladd Date: Fri, 18 Jul 2025 10:36:56 -0700 Subject: [PATCH 01/11] Implemented fix to match HubConnection.cs logic (https://github.com/dotnet/aspnetcore/issues/42806) --- src/SignalR/clients/ts/signalr/src/HubConnection.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/SignalR/clients/ts/signalr/src/HubConnection.ts b/src/SignalR/clients/ts/signalr/src/HubConnection.ts index f89d63a21e66..2487d0f0b558 100644 --- a/src/SignalR/clients/ts/signalr/src/HubConnection.ts +++ b/src/SignalR/clients/ts/signalr/src/HubConnection.ts @@ -871,7 +871,7 @@ export class HubConnection { let previousReconnectAttempts = 0; let retryError = error !== undefined ? error : new Error("Attempting to reconnect due to a unknown error."); - let nextRetryDelay = this._getNextRetryDelay(previousReconnectAttempts++, 0, retryError); + let nextRetryDelay = this._getNextRetryDelay(previousReconnectAttempts, 0, retryError); if (nextRetryDelay === null) { this._logger.log(LogLevel.Debug, "Connection not reconnecting because the IRetryPolicy returned null on the first reconnect attempt."); @@ -902,7 +902,7 @@ export class HubConnection { } while (nextRetryDelay !== null) { - this._logger.log(LogLevel.Information, `Reconnect attempt number ${previousReconnectAttempts} will start in ${nextRetryDelay} ms.`); + this._logger.log(LogLevel.Information, `Reconnect attempt number ${previousReconnectAttempts + 1} will start in ${nextRetryDelay} ms.`); await new Promise((resolve) => { this._reconnectDelayHandle = setTimeout(resolve, nextRetryDelay!); @@ -941,8 +941,9 @@ export class HubConnection { return; } + previousReconnectAttempts++; retryError = e instanceof Error ? e : new Error((e as any).toString()); - nextRetryDelay = this._getNextRetryDelay(previousReconnectAttempts++, Date.now() - reconnectStartTime, retryError); + nextRetryDelay = this._getNextRetryDelay(previousReconnectAttempts, Date.now() - reconnectStartTime, retryError); } } From 42e85a00890f8bf950d96d237b4aa6d68f1f3ec7 Mon Sep 17 00:00:00 2001 From: Scott Ladd Date: Tue, 5 Aug 2025 11:31:29 -0700 Subject: [PATCH 02/11] Added port binding exception on invalid port string. Resolves #24384 --- src/Http/Http/src/BindingAddress.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Http/Http/src/BindingAddress.cs b/src/Http/Http/src/BindingAddress.cs index b8ceef625b06..3eaa0b8d6cb6 100644 --- a/src/Http/Http/src/BindingAddress.cs +++ b/src/Http/Http/src/BindingAddress.cs @@ -217,6 +217,8 @@ public static BindingAddress Parse(string address) hasSpecifiedPort = true; host = address.Substring(schemeDelimiterEnd, portDelimiterStart - schemeDelimiterEnd); port = portNumber; + }else{ + throw new FormatException($"Invalid port: '{portString}'"); } } From 9426b00935448449a5efdabe2b5fe97f2e83c3a7 Mon Sep 17 00:00:00 2001 From: Scott Ladd Date: Tue, 5 Aug 2025 12:48:50 -0700 Subject: [PATCH 03/11] added tests for invalid port --- src/Http/Http/test/BindingAddressTests.cs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/Http/Http/test/BindingAddressTests.cs b/src/Http/Http/test/BindingAddressTests.cs index f75f483559a8..4700d4160797 100644 --- a/src/Http/Http/test/BindingAddressTests.cs +++ b/src/Http/Http/test/BindingAddressTests.cs @@ -30,6 +30,24 @@ public void FromUriThrowsForUrlsWithoutHost(string url) Assert.Throws(() => BindingAddress.Parse(url)); } + [Theory] + [InlineData("http://localhost:NOTAPORT")] + [InlineData("http://localhost:NOTAPORT/")] + [InlineData("http://localhost:NOTAPORT/random/url")] + [InlineData("https://localhost:NOTAPORT")] + [InlineData("https://localhost:NOTAPORT/")] + [InlineData("https://localhost:NOTAPORT/random/url")] + [InlineData("http://www.example.com:NOTAPORT")] + [InlineData("http://www.example.com:NOTAPORT/")] + [InlineData("http://www.example.com:NOTAPORT/foo?bar=baz")] + [InlineData("https://www.example.com:NOTAPORT")] + [InlineData("https://www.example.com:NOTAPORT/")] + [InlineData("https://www.example.com:NOTAPORT/foo?bar=baz")] + public void FromUriThrowsForUrlsWithInvalidPortNumbers(string url) + { + Assert.Throws(() => BindingAddress.Parse(url)); + } + [ConditionalTheory] [InlineData("http://unix:/")] [InlineData("http://unix:/c")] @@ -52,9 +70,6 @@ public void FromUriThrowsForUrlsWithWrongFilePathOnWindows(string url) [InlineData("http://www.example.com:5000", "http", "www.example.com", 5000, "", null)] [InlineData("https://www.example.com:5000", "https", "www.example.com", 5000, "", null)] [InlineData("http://www.example.com:5000/", "http", "www.example.com", 5000, "", "http://www.example.com:5000")] - [InlineData("http://www.example.com:NOTAPORT", "http", "www.example.com:NOTAPORT", 80, "", "http://www.example.com:notaport:80")] - [InlineData("https://www.example.com:NOTAPORT", "https", "www.example.com:NOTAPORT", 443, "", "https://www.example.com:notaport:443")] - [InlineData("http://www.example.com:NOTAPORT/", "http", "www.example.com:NOTAPORT", 80, "", "http://www.example.com:notaport:80")] [InlineData("http://foo:/tmp/kestrel-test.sock:5000/doesn't/matter", "http", "foo:", 80, "/tmp/kestrel-test.sock:5000/doesn't/matter", "http://foo::80/tmp/kestrel-test.sock:5000/doesn't/matter")] [InlineData("http://unix:foo/tmp/kestrel-test.sock", "http", "unix:foo", 80, "/tmp/kestrel-test.sock", "http://unix:foo:80/tmp/kestrel-test.sock")] [InlineData("http://unix:5000/tmp/kestrel-test.sock", "http", "unix", 5000, "/tmp/kestrel-test.sock", "http://unix:5000/tmp/kestrel-test.sock")] From 370e127bae4493e9be9d196e1a1a8d13e1c80970 Mon Sep 17 00:00:00 2001 From: Scott Ladd Date: Tue, 5 Aug 2025 14:31:42 -0700 Subject: [PATCH 04/11] Update src/Http/Http/src/BindingAddress.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Applying suggestion to match linting. Co-authored-by: Günther Foidl --- src/Http/Http/src/BindingAddress.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Http/Http/src/BindingAddress.cs b/src/Http/Http/src/BindingAddress.cs index 3eaa0b8d6cb6..748b08fd9c3b 100644 --- a/src/Http/Http/src/BindingAddress.cs +++ b/src/Http/Http/src/BindingAddress.cs @@ -217,7 +217,9 @@ public static BindingAddress Parse(string address) hasSpecifiedPort = true; host = address.Substring(schemeDelimiterEnd, portDelimiterStart - schemeDelimiterEnd); port = portNumber; - }else{ + } + else + { throw new FormatException($"Invalid port: '{portString}'"); } } From 47c95f207070ee3168f2c37464776391febd511f Mon Sep 17 00:00:00 2001 From: Scott Ladd Date: Tue, 5 Aug 2025 14:33:54 -0700 Subject: [PATCH 05/11] Update src/Http/Http/src/BindingAddress.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adding additional help message. Co-authored-by: Günther Foidl --- src/Http/Http/src/BindingAddress.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Http/src/BindingAddress.cs b/src/Http/Http/src/BindingAddress.cs index 748b08fd9c3b..f63e8e4c5a43 100644 --- a/src/Http/Http/src/BindingAddress.cs +++ b/src/Http/Http/src/BindingAddress.cs @@ -220,7 +220,7 @@ public static BindingAddress Parse(string address) } else { - throw new FormatException($"Invalid port: '{portString}'"); + throw new FormatException($"Invalid port: '{portString}, only numbers are allowed'"); } } From f0b45f6040bbf60890fc0dac77dd18dfad0bdacd Mon Sep 17 00:00:00 2001 From: Scott Ladd Date: Tue, 5 Aug 2025 15:19:36 -0700 Subject: [PATCH 06/11] Update src/Http/Http/src/BindingAddress.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I think there's still an extra ' at the end after allowed. Perhaps a . or nothing to end the message? Co-authored-by: Günther Foidl --- src/Http/Http/src/BindingAddress.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Http/src/BindingAddress.cs b/src/Http/Http/src/BindingAddress.cs index f63e8e4c5a43..0efdb2c301e2 100644 --- a/src/Http/Http/src/BindingAddress.cs +++ b/src/Http/Http/src/BindingAddress.cs @@ -220,7 +220,7 @@ public static BindingAddress Parse(string address) } else { - throw new FormatException($"Invalid port: '{portString}, only numbers are allowed'"); + throw new FormatException($"Invalid port: '{portString}', only numbers are allowed'"); } } From a46455731d7ba4834a0d6b174e02b856077944e7 Mon Sep 17 00:00:00 2001 From: Scott Ladd Date: Tue, 5 Aug 2025 15:20:31 -0700 Subject: [PATCH 07/11] adjusted message --- src/Http/Http/src/BindingAddress.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Http/src/BindingAddress.cs b/src/Http/Http/src/BindingAddress.cs index 0efdb2c301e2..9cb2886af45d 100644 --- a/src/Http/Http/src/BindingAddress.cs +++ b/src/Http/Http/src/BindingAddress.cs @@ -220,7 +220,7 @@ public static BindingAddress Parse(string address) } else { - throw new FormatException($"Invalid port: '{portString}', only numbers are allowed'"); + throw new FormatException($"Invalid port: '{portString}', only numbers are allowed."); } } From d96f01e61f47ee8be55dbb45b5d4bbd5f4ae5532 Mon Sep 17 00:00:00 2001 From: Scott Ladd Date: Tue, 5 Aug 2025 21:10:06 -0700 Subject: [PATCH 08/11] Should clear the named pipe issue --- src/Http/Http/src/BindingAddress.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Http/src/BindingAddress.cs b/src/Http/Http/src/BindingAddress.cs index 9cb2886af45d..56971d2cfd92 100644 --- a/src/Http/Http/src/BindingAddress.cs +++ b/src/Http/Http/src/BindingAddress.cs @@ -203,7 +203,7 @@ public static BindingAddress Parse(string address) var port = 0; var hasSpecifiedPort = false; - if (!isUnixPipe) + if (!isUnixPipe && !isNamedPipe) { var portDelimiterStart = address.LastIndexOf(':', pathDelimiterStart - 1, pathDelimiterStart - schemeDelimiterEnd); if (portDelimiterStart >= 0) From 07c15f0b21d7d2252b53f49dc55f5da58f369f52 Mon Sep 17 00:00:00 2001 From: Scott Ladd Date: Wed, 6 Aug 2025 00:14:14 -0700 Subject: [PATCH 09/11] removed invalid tests --- src/Http/Http/test/BindingAddressTests.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Http/Http/test/BindingAddressTests.cs b/src/Http/Http/test/BindingAddressTests.cs index 4700d4160797..2d0c568a834a 100644 --- a/src/Http/Http/test/BindingAddressTests.cs +++ b/src/Http/Http/test/BindingAddressTests.cs @@ -70,8 +70,6 @@ public void FromUriThrowsForUrlsWithWrongFilePathOnWindows(string url) [InlineData("http://www.example.com:5000", "http", "www.example.com", 5000, "", null)] [InlineData("https://www.example.com:5000", "https", "www.example.com", 5000, "", null)] [InlineData("http://www.example.com:5000/", "http", "www.example.com", 5000, "", "http://www.example.com:5000")] - [InlineData("http://foo:/tmp/kestrel-test.sock:5000/doesn't/matter", "http", "foo:", 80, "/tmp/kestrel-test.sock:5000/doesn't/matter", "http://foo::80/tmp/kestrel-test.sock:5000/doesn't/matter")] - [InlineData("http://unix:foo/tmp/kestrel-test.sock", "http", "unix:foo", 80, "/tmp/kestrel-test.sock", "http://unix:foo:80/tmp/kestrel-test.sock")] [InlineData("http://unix:5000/tmp/kestrel-test.sock", "http", "unix", 5000, "/tmp/kestrel-test.sock", "http://unix:5000/tmp/kestrel-test.sock")] public void UrlsAreParsedCorrectly(string url, string scheme, string host, int port, string pathBase, string toString) { From cea5a9c851dccc43d38ca1d79a2ee24174f99eb6 Mon Sep 17 00:00:00 2001 From: Scott Ladd Date: Wed, 6 Aug 2025 09:47:47 -0700 Subject: [PATCH 10/11] Remove port parse for IPv6 loop back address. --- src/Http/Http/src/BindingAddress.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Http/Http/src/BindingAddress.cs b/src/Http/Http/src/BindingAddress.cs index 56971d2cfd92..cc1ffbdb83ff 100644 --- a/src/Http/Http/src/BindingAddress.cs +++ b/src/Http/Http/src/BindingAddress.cs @@ -12,6 +12,7 @@ public class BindingAddress { private const string UnixPipeHostPrefix = "unix:/"; private const string NamedPipeHostPrefix = "pipe:/"; + private const string IPv6LoopBackPrefix = "[::1]"; private BindingAddress(string host, string pathBase, int port, string scheme) { @@ -163,6 +164,7 @@ public static BindingAddress Parse(string address) var isUnixPipe = address.IndexOf(UnixPipeHostPrefix, schemeDelimiterEnd, StringComparison.Ordinal) == schemeDelimiterEnd; var isNamedPipe = address.IndexOf(NamedPipeHostPrefix, schemeDelimiterEnd, StringComparison.Ordinal) == schemeDelimiterEnd; + var isIPv6LoopBack = address.IndexOf(IPv6LoopBackPrefix, schemeDelimiterEnd, StringComparison.Ordinal) == schemeDelimiterEnd; int pathDelimiterStart; int pathDelimiterEnd; @@ -206,7 +208,7 @@ public static BindingAddress Parse(string address) if (!isUnixPipe && !isNamedPipe) { var portDelimiterStart = address.LastIndexOf(':', pathDelimiterStart - 1, pathDelimiterStart - schemeDelimiterEnd); - if (portDelimiterStart >= 0) + if (portDelimiterStart >= 0 && !isIPv6LoopBack) { var portDelimiterEnd = portDelimiterStart + ":".Length; From 6fdaf1792f2629ce41b1b9aaad7b9b9745e80538 Mon Sep 17 00:00:00 2001 From: Scott Ladd Date: Wed, 6 Aug 2025 11:16:49 -0700 Subject: [PATCH 11/11] adjust for IPv6 --- src/Http/Http/src/BindingAddress.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Http/Http/src/BindingAddress.cs b/src/Http/Http/src/BindingAddress.cs index cc1ffbdb83ff..20cc26ee74fd 100644 --- a/src/Http/Http/src/BindingAddress.cs +++ b/src/Http/Http/src/BindingAddress.cs @@ -12,7 +12,6 @@ public class BindingAddress { private const string UnixPipeHostPrefix = "unix:/"; private const string NamedPipeHostPrefix = "pipe:/"; - private const string IPv6LoopBackPrefix = "[::1]"; private BindingAddress(string host, string pathBase, int port, string scheme) { @@ -164,7 +163,6 @@ public static BindingAddress Parse(string address) var isUnixPipe = address.IndexOf(UnixPipeHostPrefix, schemeDelimiterEnd, StringComparison.Ordinal) == schemeDelimiterEnd; var isNamedPipe = address.IndexOf(NamedPipeHostPrefix, schemeDelimiterEnd, StringComparison.Ordinal) == schemeDelimiterEnd; - var isIPv6LoopBack = address.IndexOf(IPv6LoopBackPrefix, schemeDelimiterEnd, StringComparison.Ordinal) == schemeDelimiterEnd; int pathDelimiterStart; int pathDelimiterEnd; @@ -207,8 +205,9 @@ public static BindingAddress Parse(string address) var hasSpecifiedPort = false; if (!isUnixPipe && !isNamedPipe) { + // Verify not a loopback uri. var portDelimiterStart = address.LastIndexOf(':', pathDelimiterStart - 1, pathDelimiterStart - schemeDelimiterEnd); - if (portDelimiterStart >= 0 && !isIPv6LoopBack) + if (portDelimiterStart >= 0 && address.Substring(pathDelimiterStart - 1, 1) != "]") { var portDelimiterEnd = portDelimiterStart + ":".Length;