From 4ba813f5a9744e5596bc2bfc5ab3e2e78fd68a1a Mon Sep 17 00:00:00 2001 From: Nikolai Norum Hansen Date: Mon, 14 Aug 2023 20:47:47 +0200 Subject: [PATCH] Enable websocket polling for netcore --- .../Halibut.Tests.DotMemory.csproj | 2 +- .../Halibut.Tests.DotMemory/MemoryFixture.cs | 2 - .../BindCertificatesForAllTests.cs | 2 - source/Halibut.Tests/Halibut.Tests.csproj | 2 +- .../Support/ServiceConnectionType.cs | 10 ++--- source/Halibut/Halibut.csproj | 2 +- source/Halibut/HalibutRuntime.cs | 4 -- .../Transport/SecureWebSocketClient.cs | 2 - .../Transport/ServerCertificateInterceptor.cs | 4 +- .../Transport/WebSocketConnectionFactory.cs | 38 ++++++++++++++++--- 10 files changed, 41 insertions(+), 27 deletions(-) diff --git a/source/Halibut.Tests.DotMemory/Halibut.Tests.DotMemory.csproj b/source/Halibut.Tests.DotMemory/Halibut.Tests.DotMemory.csproj index 2bf7f505..fc8c99c0 100644 --- a/source/Halibut.Tests.DotMemory/Halibut.Tests.DotMemory.csproj +++ b/source/Halibut.Tests.DotMemory/Halibut.Tests.DotMemory.csproj @@ -43,7 +43,7 @@ - $(DefineConstants);SUPPORTS_WEB_SOCKET_CLIENT;DOES_NOT_SUPPORT_CANCELLATION_ON_SOCKETS + $(DefineConstants);DOES_NOT_SUPPORT_CANCELLATION_ON_SOCKETS diff --git a/source/Halibut.Tests.DotMemory/MemoryFixture.cs b/source/Halibut.Tests.DotMemory/MemoryFixture.cs index f6e5890f..efc9db49 100644 --- a/source/Halibut.Tests.DotMemory/MemoryFixture.cs +++ b/source/Halibut.Tests.DotMemory/MemoryFixture.cs @@ -59,10 +59,8 @@ public void TcpClientsAreDisposedCorrectly() RunPollingClient(server, Certificates.TentaclePolling, Certificates.TentaclePollingPublicThumbprint); } -#if SUPPORTS_WEB_SOCKET_CLIENT for (var i = 0; i < NumberOfClients; i++) RunWebSocketPollingClient(server, Certificates.TentaclePolling, Certificates.TentaclePollingPublicThumbprint, Certificates.OctopusPublicThumbprint); -#endif //https://dotnettools-support.jetbrains.com/hc/en-us/community/posts/360000088690-How-reproduce-DotMemory-s-Force-GC-button-s-behaviour-on-code-with-c-?page=1#community_comment_360000072750 for (var i = 0; i < 4; i++) diff --git a/source/Halibut.Tests/BindCertificatesForAllTests.cs b/source/Halibut.Tests/BindCertificatesForAllTests.cs index 52317e20..057be09b 100644 --- a/source/Halibut.Tests/BindCertificatesForAllTests.cs +++ b/source/Halibut.Tests/BindCertificatesForAllTests.cs @@ -11,9 +11,7 @@ public class TestsSetupClass [OneTimeSetUp] public void GlobalSetup() { -#if SUPPORTS_WEB_SOCKET_CLIENT WebSocketSslCertificateHelper.AddSslCertToLocalStore(); -#endif } } } diff --git a/source/Halibut.Tests/Halibut.Tests.csproj b/source/Halibut.Tests/Halibut.Tests.csproj index 60573996..94de356e 100644 --- a/source/Halibut.Tests/Halibut.Tests.csproj +++ b/source/Halibut.Tests/Halibut.Tests.csproj @@ -49,7 +49,7 @@ - $(DefineConstants);SUPPORTS_WEB_SOCKET_CLIENT;DOES_NOT_SUPPORT_CANCELLATION_ON_SOCKETS + $(DefineConstants);DOES_NOT_SUPPORT_CANCELLATION_ON_SOCKETS diff --git a/source/Halibut.Tests/Support/ServiceConnectionType.cs b/source/Halibut.Tests/Support/ServiceConnectionType.cs index 9e4dfcbd..823e7a50 100644 --- a/source/Halibut.Tests/Support/ServiceConnectionType.cs +++ b/source/Halibut.Tests/Support/ServiceConnectionType.cs @@ -10,12 +10,10 @@ public enum ServiceConnectionType public static class ServiceConnectionTypes { public static ServiceConnectionType[] All => new[] - { - ServiceConnectionType.Listening, - ServiceConnectionType.Polling, -#if SUPPORTS_WEB_SOCKET_CLIENT - ServiceConnectionType.PollingOverWebSocket -#endif + { + ServiceConnectionType.Listening, + ServiceConnectionType.Polling, + ServiceConnectionType.PollingOverWebSocket }; public static ServiceConnectionType[] AllExceptWebSockets => new[] diff --git a/source/Halibut/Halibut.csproj b/source/Halibut/Halibut.csproj index d380d957..c2d3a752 100644 --- a/source/Halibut/Halibut.csproj +++ b/source/Halibut/Halibut.csproj @@ -55,7 +55,7 @@ - $(DefineConstants);HAS_REAL_PROXY;SUPPORTS_WEB_SOCKET_CLIENT + $(DefineConstants);HAS_REAL_PROXY diff --git a/source/Halibut/HalibutRuntime.cs b/source/Halibut/HalibutRuntime.cs index a971477c..a33c8bd1 100644 --- a/source/Halibut/HalibutRuntime.cs +++ b/source/Halibut/HalibutRuntime.cs @@ -186,11 +186,7 @@ public void Poll(Uri subscription, ServiceEndPoint endPoint, CancellationToken c var log = logs.ForEndpoint(endPoint.BaseUri); if (endPoint.IsWebSocketEndpoint) { -#if SUPPORTS_WEB_SOCKET_CLIENT client = new SecureWebSocketClient(ExchangeProtocolBuilder(), endPoint, serverCertificate, TimeoutsAndLimits, log, connectionManager); -#else - throw new NotSupportedException("The netstandard build of this library cannot act as the client in a WebSocket polling setup"); -#endif } else { diff --git a/source/Halibut/Transport/SecureWebSocketClient.cs b/source/Halibut/Transport/SecureWebSocketClient.cs index d5fc58b9..52acb1b7 100644 --- a/source/Halibut/Transport/SecureWebSocketClient.cs +++ b/source/Halibut/Transport/SecureWebSocketClient.cs @@ -4,7 +4,6 @@ // See https://github.com/dotnet/corefx/issues/12038 using Halibut.Util; -#if SUPPORTS_WEB_SOCKET_CLIENT using System; using System.Diagnostics; using System.Net.Sockets; @@ -255,4 +254,3 @@ void HandleError(Exception lastError, bool retryAllowed) } } } -#endif diff --git a/source/Halibut/Transport/ServerCertificateInterceptor.cs b/source/Halibut/Transport/ServerCertificateInterceptor.cs index 8cbc96d7..62ac4f1d 100644 --- a/source/Halibut/Transport/ServerCertificateInterceptor.cs +++ b/source/Halibut/Transport/ServerCertificateInterceptor.cs @@ -1,4 +1,3 @@ -#if SUPPORTS_WEB_SOCKET_CLIENT using System; using System.Collections.Generic; using System.Net; @@ -68,5 +67,4 @@ public static void Validate(string connectionId, ServiceEndPoint endPoint) throw new UnexpectedCertificateException(providedCertificate, endPoint); } } -} -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/source/Halibut/Transport/WebSocketConnectionFactory.cs b/source/Halibut/Transport/WebSocketConnectionFactory.cs index 3b12a5fd..6ec5c50a 100644 --- a/source/Halibut/Transport/WebSocketConnectionFactory.cs +++ b/source/Halibut/Transport/WebSocketConnectionFactory.cs @@ -1,6 +1,6 @@ -#if SUPPORTS_WEB_SOCKET_CLIENT using System; using System.Net; +using System.Net.Security; using System.Net.WebSockets; using System.Security.Cryptography.X509Certificates; using System.Threading; @@ -72,19 +72,30 @@ ClientWebSocket CreateConnectedClient(ServiceEndPoint serviceEndpoint, Cancellat client.Options.ClientCertificates = new X509Certificate2Collection(new X509Certificate2Collection(clientCertificate)); client.Options.AddSubProtocol("Octopus"); client.Options.SetRequestHeader(ServerCertificateInterceptor.Header, connectionId); +#if NETCOREAPP + client.Options.RemoteCertificateValidationCallback = (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) + => ValidateRemoteCert(serviceEndpoint, certificate, sslPolicyErrors); +#endif + + if (serviceEndpoint.Proxy != null) client.Options.Proxy = new WebSocketProxy(serviceEndpoint.Proxy); try { +#if !NETCOREAPP ServerCertificateInterceptor.Expect(connectionId); +#endif using (var cts = new CancellationTokenSource(serviceEndpoint.TcpClientConnectTimeout)) { using (cancellationToken.Register(() => cts?.Cancel())) client.ConnectAsync(serviceEndpoint.BaseUri, cts.Token) .ConfigureAwait(false).GetAwaiter().GetResult(); } + +#if !NETCOREAPP ServerCertificateInterceptor.Validate(connectionId, serviceEndpoint); +#endif } catch { @@ -96,14 +107,16 @@ ClientWebSocket CreateConnectedClient(ServiceEndPoint serviceEndpoint, Cancellat client.Dispose(); throw; } +#if !NETCOREAPP finally { ServerCertificateInterceptor.Remove(connectionId); } +#endif return client; } - + async Task CreateConnectedClientAsync(ServiceEndPoint serviceEndpoint, CancellationToken cancellationToken) { if (!serviceEndpoint.IsWebSocketEndpoint) @@ -117,10 +130,16 @@ async Task CreateConnectedClientAsync(ServiceEndPoint serviceEn client.Options.SetRequestHeader(ServerCertificateInterceptor.Header, connectionId); if (serviceEndpoint.Proxy != null) client.Options.Proxy = new WebSocketProxy(serviceEndpoint.Proxy); +#if NETCOREAPP + client.Options.RemoteCertificateValidationCallback = (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) + => ValidateRemoteCert(serviceEndpoint, certificate, sslPolicyErrors); +#endif try { +#if !NETCOREAPP ServerCertificateInterceptor.Expect(connectionId); +#endif using (var cts = new CancellationTokenSource(serviceEndpoint.TcpClientConnectTimeout)) { using (cancellationToken.Register(() => cts?.Cancel())) @@ -128,7 +147,9 @@ async Task CreateConnectedClientAsync(ServiceEndPoint serviceEn await client.ConnectAsync(serviceEndpoint.BaseUri, cts.Token); } } +#if !NETCOREAPP ServerCertificateInterceptor.Validate(connectionId, serviceEndpoint); +#endif } catch { @@ -140,13 +161,21 @@ async Task CreateConnectedClientAsync(ServiceEndPoint serviceEn client.Dispose(); throw; } +#if !NETCOREAPP finally { ServerCertificateInterceptor.Remove(connectionId); } - +#endif return client; } + + private static bool ValidateRemoteCert(ServiceEndPoint serviceEndPoint, X509Certificate certificate, SslPolicyErrors sslPolicyErrors) + { + if (certificate == null) return false; + + return new X509Certificate2(certificate).Thumbprint == serviceEndPoint.RemoteThumbprint; + } } class WebSocketProxy : IWebProxy @@ -172,5 +201,4 @@ public WebSocketProxy(ProxyDetails proxy) public ICredentials Credentials { get; set; } } -} -#endif \ No newline at end of file +} \ No newline at end of file