From 970d0f5d00c539501eeb48f98be7e0348a88d354 Mon Sep 17 00:00:00 2001 From: Brennan Date: Thu, 10 Sep 2020 18:22:14 -0700 Subject: [PATCH] [SignalR] Copy cookies from negotiate to WebSockets (#24572) --- .../FunctionalTests/HubConnectionTests.cs | 28 +++++++++++++++++++ .../Client/test/FunctionalTests/Startup.cs | 12 ++++++++ .../src/HttpConnection.Log.cs | 8 ++++++ .../src/HttpConnection.cs | 17 +++++++---- 4 files changed, 60 insertions(+), 5 deletions(-) diff --git a/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubConnectionTests.cs b/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubConnectionTests.cs index 1e1de9abe4..216a39b1c2 100644 --- a/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubConnectionTests.cs +++ b/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubConnectionTests.cs @@ -1721,6 +1721,34 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests } } + [ConditionalFact] + [WebSocketsSupportedCondition] + public async Task CookiesFromNegotiateAreAppliedToWebSockets() + { + await using (var server = await StartServer()) + { + var hubConnection = new HubConnectionBuilder() + .WithLoggerFactory(LoggerFactory) + .WithUrl(server.Url + "/default", HttpTransportType.WebSockets) + .Build(); + try + { + await hubConnection.StartAsync().OrTimeout(); + var cookieValue = await hubConnection.InvokeAsync(nameof(TestHub.GetCookieValue), "fromNegotiate").OrTimeout(); + Assert.Equal("a value", cookieValue); + } + catch (Exception ex) + { + LoggerFactory.CreateLogger().LogError(ex, "{ExceptionType} from test", ex.GetType().FullName); + throw; + } + finally + { + await hubConnection.DisposeAsync().OrTimeout(); + } + } + } + [Fact] public async Task CheckHttpConnectionFeatures() { diff --git a/src/SignalR/clients/csharp/Client/test/FunctionalTests/Startup.cs b/src/SignalR/clients/csharp/Client/test/FunctionalTests/Startup.cs index 14a7d7016f..23bf2e3035 100644 --- a/src/SignalR/clients/csharp/Client/test/FunctionalTests/Startup.cs +++ b/src/SignalR/clients/csharp/Client/test/FunctionalTests/Startup.cs @@ -65,6 +65,18 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests app.UseAuthentication(); app.UseAuthorization(); + app.Use(next => + { + return context => + { + if (context.Request.Path.Value.EndsWith("/negotiate")) + { + context.Response.Cookies.Append("fromNegotiate", "a value"); + } + return next(context); + }; + }); + app.UseEndpoints(endpoints => { endpoints.MapHub("/default"); diff --git a/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnection.Log.cs b/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnection.Log.cs index 74b3de2e71..9169f5f745 100644 --- a/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnection.Log.cs +++ b/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnection.Log.cs @@ -69,6 +69,9 @@ namespace Microsoft.AspNetCore.Http.Connections.Client private static readonly Action _serverSentEventsNotSupportedByBrowser = LoggerMessage.Define(LogLevel.Debug, new EventId(19, "ServerSentEventsNotSupportedByBrowser"), "Skipping ServerSentEvents because they are not supported by the browser."); + private static readonly Action _cookiesNotSupported = + LoggerMessage.Define(LogLevel.Trace, new EventId(20, "CookiesNotSupported"), "Cookies are not supported on this platform."); + public static void Starting(ILogger logger) { _starting(logger, null); @@ -175,6 +178,11 @@ namespace Microsoft.AspNetCore.Http.Connections.Client { _serverSentEventsNotSupportedByBrowser(logger, null); } + + public static void CookiesNotSupported(ILogger logger) + { + _cookiesNotSupported(logger, null); + } } } } diff --git a/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnection.cs b/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnection.cs index ecd2c30b8b..1af8f917f8 100644 --- a/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnection.cs +++ b/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnection.cs @@ -154,7 +154,6 @@ namespace Microsoft.AspNetCore.Http.Connections.Client _isRunningInBrowser = Utils.IsRunningInBrowser(); - if (httpConnectionOptions.Transports == HttpTransportType.ServerSentEvents && _isRunningInBrowser) { throw new ArgumentException("ServerSentEvents can not be the only transport specified when running in the browser.", nameof(httpConnectionOptions)); @@ -537,13 +536,21 @@ namespace Microsoft.AspNetCore.Http.Connections.Client httpClientHandler.Proxy = _httpConnectionOptions.Proxy; } - // Only access HttpClientHandler.ClientCertificates and HttpClientHandler.CookieContainer - // if the user has configured those options - // Some variants of Mono do not support client certs or cookies and will throw NotImplementedException - if (_httpConnectionOptions.Cookies.Count > 0) + try { + // On supported platforms, we need to pass the cookie container to the http client + // so that we can capture any cookies from the negotiate response and give them to WebSockets. httpClientHandler.CookieContainer = _httpConnectionOptions.Cookies; } + // Some variants of Mono do not support client certs or cookies and will throw NotImplementedException or NotSupportedException + // Also WASM doesn't support some settings in the browser + catch (Exception ex) when (ex is NotSupportedException || ex is NotImplementedException) + { + Log.CookiesNotSupported(_logger); + } + + // Only access HttpClientHandler.ClientCertificates + // if the user has configured those options // https://github.com/aspnet/SignalR/issues/2232 var clientCertificates = _httpConnectionOptions.ClientCertificates; if (clientCertificates?.Count > 0)