Fix SignalR+WebSockets on WASM (#25922)

* Fix SignalR+WebSockets on WASM

* fb

* move stuff

* select
This commit is contained in:
Brennan 2020-09-17 11:30:31 -07:00 committed by GitHub
parent e51468db03
commit 899deacb22
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 86 additions and 47 deletions

View File

@ -7,7 +7,9 @@ using BasicTestApp;
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure;
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
using Microsoft.AspNetCore.E2ETesting;
using Microsoft.AspNetCore.Http.Connections;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
using TestServer;
using Xunit;
using Xunit.Abstractions;
@ -38,13 +40,28 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
}
[Fact]
public void SignalRClientWorks()
public void SignalRClientWorksWithLongPolling()
{
Browser.FindElement(By.Id("hub-url")).SendKeys(
new Uri(_apiServerFixture.RootUri, "/subdir/chathub").AbsoluteUri);
var target = new SelectElement(Browser.FindElement(By.Id("transport-type")));
target.SelectByText("LongPolling");
Browser.FindElement(By.Id("hub-connect")).Click();
Browser.Equal("SignalR Client: Echo",
Browser.Equal("SignalR Client: Echo LongPolling",
() => Browser.FindElements(By.CssSelector("li")).FirstOrDefault()?.Text);
}
[Fact]
public void SignalRClientWorksWithWebSockets()
{
Browser.FindElement(By.Id("hub-url")).SendKeys(
new Uri(_apiServerFixture.RootUri, "/subdir/chathub").AbsoluteUri);
var target = new SelectElement(Browser.FindElement(By.Id("transport-type")));
target.SelectByText("WebSockets");
Browser.FindElement(By.Id("hub-connect")).Click();
Browser.Equal("SignalR Client: Echo WebSockets",
() => Browser.FindElements(By.CssSelector("li")).FirstOrDefault()?.Text);
}
}

View File

@ -1,4 +1,5 @@
@using Microsoft.AspNetCore.SignalR.Client
@using Microsoft.AspNetCore.Http.Connections
<h1 id="signalr-client">SignalR Client</h1>
@ -7,6 +8,10 @@
<p>
Hub URL:
<input id="hub-url" @bind="hubUrl" />
<select id="transport-type" @bind="transportType">
<option value=@HttpTransportType.LongPolling>LongPolling</option>
<option value=@HttpTransportType.WebSockets>WebSockets</option>
</select>
<button id="hub-connect" @onclick="Connect">Connect</button>
</p>
@ -21,13 +26,14 @@
@code {
private string hubUrl;
private HttpTransportType transportType;
private HubConnection hubConnection;
private List<string> messages = new List<string>();
protected async Task Connect()
{
hubConnection = new HubConnectionBuilder()
.WithUrl(hubUrl)
.WithUrl(hubUrl, transportType)
.Build();
hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
@ -38,7 +44,7 @@
});
await hubConnection.StartAsync();
await hubConnection.SendAsync("SendMessage", "SignalR Client", "Echo");
await hubConnection.SendAsync("SendMessage", "SignalR Client", $"Echo {transportType}");
}
public bool IsConnected =>

View File

@ -39,8 +39,9 @@ namespace Microsoft.AspNetCore.Http.Connections.Client.Internal
{
return new WebSocketsTransport(_httpConnectionOptions, _loggerFactory, _accessTokenProvider);
}
catch (PlatformNotSupportedException)
catch (PlatformNotSupportedException ex)
{
Log.TransportNotSupported(_loggerFactory.CreateLogger<DefaultTransportFactory>(), HttpTransportType.WebSockets, ex);
_websocketsSupported = false;
}
}
@ -59,5 +60,16 @@ namespace Microsoft.AspNetCore.Http.Connections.Client.Internal
throw new InvalidOperationException("No requested transports available on the server.");
}
private static class Log
{
private static readonly Action<ILogger, HttpTransportType, Exception> _transportNotSupported =
LoggerMessage.Define<HttpTransportType>(LogLevel.Debug, new EventId(1, "TransportNotSupported"), "Transport '{TransportType}' is not supported.");
public static void TransportNotSupported(ILogger logger, HttpTransportType transportType, Exception ex)
{
_transportNotSupported(logger, transportType, ex);
}
}
}
}

View File

@ -37,66 +37,70 @@ namespace Microsoft.AspNetCore.Http.Connections.Client.Internal
public WebSocketsTransport(HttpConnectionOptions httpConnectionOptions, ILoggerFactory loggerFactory, Func<Task<string>> accessTokenProvider)
{
_webSocket = new ClientWebSocket();
_isRunningInBrowser = Utils.IsRunningInBrowser();
// Full Framework will throw when trying to set the User-Agent header
// So avoid setting it in netstandard2.0 and only set it in netstandard2.1 and higher
// ClientWebSocketOptions throws PNSE when accessing and setting properties
if (!_isRunningInBrowser)
{
// Full Framework will throw when trying to set the User-Agent header
// So avoid setting it in netstandard2.0 and only set it in netstandard2.1 and higher
#if !NETSTANDARD2_0
_webSocket.Options.SetRequestHeader("User-Agent", Constants.UserAgentHeader.ToString());
_webSocket.Options.SetRequestHeader("User-Agent", Constants.UserAgentHeader.ToString());
#else
// Set an alternative user agent header on Full framework
_webSocket.Options.SetRequestHeader("X-SignalR-User-Agent", Constants.UserAgentHeader.ToString());
// Set an alternative user agent header on Full framework
_webSocket.Options.SetRequestHeader("X-SignalR-User-Agent", Constants.UserAgentHeader.ToString());
#endif
if (httpConnectionOptions != null)
{
if (httpConnectionOptions.Headers != null)
if (httpConnectionOptions != null)
{
foreach (var header in httpConnectionOptions.Headers)
if (httpConnectionOptions.Headers != null)
{
_webSocket.Options.SetRequestHeader(header.Key, header.Value);
foreach (var header in httpConnectionOptions.Headers)
{
_webSocket.Options.SetRequestHeader(header.Key, header.Value);
}
}
if (httpConnectionOptions.Cookies != null)
{
_webSocket.Options.Cookies = httpConnectionOptions.Cookies;
}
if (httpConnectionOptions.ClientCertificates != null)
{
_webSocket.Options.ClientCertificates.AddRange(httpConnectionOptions.ClientCertificates);
}
if (httpConnectionOptions.Credentials != null)
{
_webSocket.Options.Credentials = httpConnectionOptions.Credentials;
}
if (httpConnectionOptions.Proxy != null)
{
_webSocket.Options.Proxy = httpConnectionOptions.Proxy;
}
if (httpConnectionOptions.UseDefaultCredentials != null)
{
_webSocket.Options.UseDefaultCredentials = httpConnectionOptions.UseDefaultCredentials.Value;
}
httpConnectionOptions.WebSocketConfiguration?.Invoke(_webSocket.Options);
}
if (httpConnectionOptions.Cookies != null)
{
_webSocket.Options.Cookies = httpConnectionOptions.Cookies;
}
if (httpConnectionOptions.ClientCertificates != null)
{
_webSocket.Options.ClientCertificates.AddRange(httpConnectionOptions.ClientCertificates);
}
if (httpConnectionOptions.Credentials != null)
{
_webSocket.Options.Credentials = httpConnectionOptions.Credentials;
}
if (httpConnectionOptions.Proxy != null)
{
_webSocket.Options.Proxy = httpConnectionOptions.Proxy;
}
if (httpConnectionOptions.UseDefaultCredentials != null)
{
_webSocket.Options.UseDefaultCredentials = httpConnectionOptions.UseDefaultCredentials.Value;
}
httpConnectionOptions.WebSocketConfiguration?.Invoke(_webSocket.Options);
_closeTimeout = httpConnectionOptions.CloseTimeout;
// Set this header so the server auth middleware will set an Unauthorized instead of Redirect status code
// See: https://github.com/aspnet/Security/blob/ff9f145a8e89c9756ea12ff10c6d47f2f7eb345f/src/Microsoft.AspNetCore.Authentication.Cookies/Events/CookieAuthenticationEvents.cs#L42
_webSocket.Options.SetRequestHeader("X-Requested-With", "XMLHttpRequest");
}
// Set this header so the server auth middleware will set an Unauthorized instead of Redirect status code
// See: https://github.com/aspnet/Security/blob/ff9f145a8e89c9756ea12ff10c6d47f2f7eb345f/src/Microsoft.AspNetCore.Authentication.Cookies/Events/CookieAuthenticationEvents.cs#L42
_webSocket.Options.SetRequestHeader("X-Requested-With", "XMLHttpRequest");
_closeTimeout = httpConnectionOptions?.CloseTimeout ?? default;
_logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger<WebSocketsTransport>();
// Ignore the HttpConnectionOptions access token provider. We were given an updated delegate from the HttpConnection.
_accessTokenProvider = accessTokenProvider;
_isRunningInBrowser = Utils.IsRunningInBrowser();
}
public async Task StartAsync(Uri url, TransferFormat transferFormat, CancellationToken cancellationToken = default)