Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/Components/test/E2ETest/Tests/SignalRClientTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,21 @@ public void SignalRClientWorksWithWebSockets()
Browser.Equal("SignalR Client: Echo WebSockets",
() => Browser.FindElements(By.CssSelector("li")).FirstOrDefault()?.Text);
}

[Fact]
public void SignalRClientSendsUserAgent()
{
Browser.Exists(By.Id("hub-url")).SendKeys(
new Uri(_apiServerFixture.RootUri, "/subdir/chathub").AbsoluteUri);
var target = new SelectElement(Browser.Exists(By.Id("transport-type")));
target.SelectByText("LongPolling");
Browser.Exists(By.Id("hub-connect")).Click();

Browser.Equal("SignalR Client: Echo LongPolling",
() => Browser.FindElements(By.CssSelector("li")).FirstOrDefault()?.Text);

Browser.Exists(By.Id("hub-useragent")).Click();
Assert.NotNull(Browser.FindElement(By.Id("useragent")).Text);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@
<option value=@HttpTransportType.WebSockets>WebSockets</option>
</select>
<button id="hub-connect" @onclick="Connect">Connect</button>
<button id="hub-useragent" @onclick="GetUserAgentHeader">GetUserAgent</button>
</p>

<div>Connected: @IsConnected</div>
<div id="useragent">User-Agent: @userAgent</div>

<ul id="messagesList">
@foreach (var message in messages)
Expand All @@ -29,6 +31,7 @@
private HttpTransportType transportType;
private HubConnection hubConnection;
private List<string> messages = new List<string>();
private string userAgent;

protected async Task Connect()
{
Expand All @@ -47,6 +50,12 @@
await hubConnection.SendAsync("SendMessage", "SignalR Client", $"Echo {transportType}");
}

protected async Task GetUserAgentHeader()
{
userAgent = await hubConnection.InvokeAsync<string>("GetHeaderValue", "X-SignalR-User-Agent");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like you want both user agents right? To make sure the normal is set as well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normal is set by the browser, we shouldn't be testing browser behavior

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So before our code was just ineffective

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In WASM it was ineffective in some browsers, and set in others
Details at #29183 (comment)

StateHasChanged();
}

public bool IsConnected =>
hubConnection != null && hubConnection.State == HubConnectionState.Connected;

Expand Down
21 changes: 21 additions & 0 deletions src/Components/test/testassets/TestServer/ChatHub.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;

Expand All @@ -12,5 +13,25 @@ public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}

public string GetHeaderValue(string headerName)
{
var context = Context.GetHttpContext();

if (context == null)
{
throw new InvalidOperationException("Unable to get HttpContext from request.");
}

var headers = context.Request.Headers;

if (headers == null)
{
throw new InvalidOperationException("Unable to get headers from context.");
}

headers.TryGetValue(headerName, out var val);
return val.ToString();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -597,8 +597,7 @@ private HttpClient CreateHttpClient()
var httpClient = new HttpClient(httpMessageHandler);
httpClient.Timeout = HttpClientTimeout;

// Start with the user agent header
httpClient.DefaultRequestHeaders.Add(Constants.UserAgent, Constants.UserAgentHeader);
var userSetUserAgent = false;

// Apply any headers configured on the HttpConnectionOptions
if (_httpConnectionOptions?.Headers != null)
Expand All @@ -608,6 +607,7 @@ private HttpClient CreateHttpClient()
// Check if the key is User-Agent and remove if empty string then replace if it exists.
if (string.Equals(header.Key, Constants.UserAgent, StringComparison.OrdinalIgnoreCase))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In WASM, the user would need to set "X-SignalR-User-Agent", whereas outside of WASM they need to set "User-Agent" to override the default we set. I could see us keeping this behavior, or saying "User-Agent" will apply to both.

{
userSetUserAgent = true;
if (string.IsNullOrEmpty(header.Value))
{
httpClient.DefaultRequestHeaders.Remove(header.Key);
Expand All @@ -629,6 +629,14 @@ private HttpClient CreateHttpClient()
}
}

// Apply default user agent only if user hasn't specified one (empty or not)
// Don't pre-emptively set this, some frameworks (mono) have different user agent format rules,
// so allowing a user to set an empty one avoids throwing on those frameworks.
if (!userSetUserAgent)
{
httpClient.DefaultRequestHeaders.Add(Constants.UserAgent, Constants.UserAgentHeader);
}

httpClient.DefaultRequestHeaders.Remove("X-Requested-With");
// Tell auth middleware to 401 instead of redirecting
httpClient.DefaultRequestHeaders.Add("X-Requested-With", "XMLHttpRequest");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@ namespace Microsoft.AspNetCore.Http.Connections.Client.Internal
{
internal static class Constants
{
public const string UserAgent = "User-Agent";
public static readonly string UserAgent = "User-Agent";
public static readonly string UserAgentHeader;

static Constants()
{
if (OperatingSystem.IsBrowser())
{
UserAgent = "X-SignalR-User-Agent";
}

var assemblyVersion = typeof(Constants)
.Assembly
.GetCustomAttributes<AssemblyInformationalVersionAttribute>()
Expand Down