From 322b10f13f43a01f6ad3c59509bbfbcb23f9d1bd Mon Sep 17 00:00:00 2001
From: BrennanConroy
Date: Mon, 8 Mar 2021 18:17:37 -0800
Subject: [PATCH 1/2] Fix User-Agent usage in SignalR
---
.../Http.Connections.Client/src/HttpConnection.cs | 12 ++++++++++--
.../src/Internal/Constants.cs | 7 ++++++-
2 files changed, 16 insertions(+), 3 deletions(-)
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 6bd1cd88fdee..bb0f786db1df 100644
--- a/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnection.cs
+++ b/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnection.cs
@@ -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)
@@ -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))
{
+ userSetUserAgent = true;
if (string.IsNullOrEmpty(header.Value))
{
httpClient.DefaultRequestHeaders.Remove(header.Key);
@@ -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");
diff --git a/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/Constants.cs b/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/Constants.cs
index 862772a2a492..ed6687834944 100644
--- a/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/Constants.cs
+++ b/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/Constants.cs
@@ -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()
From 993b5b774fe5ae51b99200b707658c6487dfeda8 Mon Sep 17 00:00:00 2001
From: BrennanConroy
Date: Tue, 9 Mar 2021 11:00:17 -0800
Subject: [PATCH 2/2] test?
---
.../test/E2ETest/Tests/SignalRClientTest.cs | 16 ++++++++++++++
.../BasicTestApp/SignalRClientComponent.razor | 9 ++++++++
.../test/testassets/TestServer/ChatHub.cs | 21 +++++++++++++++++++
3 files changed, 46 insertions(+)
diff --git a/src/Components/test/E2ETest/Tests/SignalRClientTest.cs b/src/Components/test/E2ETest/Tests/SignalRClientTest.cs
index 9265abfc1e1f..82e313f2cd76 100644
--- a/src/Components/test/E2ETest/Tests/SignalRClientTest.cs
+++ b/src/Components/test/E2ETest/Tests/SignalRClientTest.cs
@@ -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);
+ }
}
}
diff --git a/src/Components/test/testassets/BasicTestApp/SignalRClientComponent.razor b/src/Components/test/testassets/BasicTestApp/SignalRClientComponent.razor
index a8fae272ab21..dd3a39833169 100644
--- a/src/Components/test/testassets/BasicTestApp/SignalRClientComponent.razor
+++ b/src/Components/test/testassets/BasicTestApp/SignalRClientComponent.razor
@@ -13,9 +13,11 @@
+
Connected: @IsConnected
+User-Agent: @userAgent
@foreach (var message in messages)
@@ -29,6 +31,7 @@
private HttpTransportType transportType;
private HubConnection hubConnection;
private List messages = new List();
+ private string userAgent;
protected async Task Connect()
{
@@ -47,6 +50,12 @@
await hubConnection.SendAsync("SendMessage", "SignalR Client", $"Echo {transportType}");
}
+ protected async Task GetUserAgentHeader()
+ {
+ userAgent = await hubConnection.InvokeAsync("GetHeaderValue", "X-SignalR-User-Agent");
+ StateHasChanged();
+ }
+
public bool IsConnected =>
hubConnection != null && hubConnection.State == HubConnectionState.Connected;
diff --git a/src/Components/test/testassets/TestServer/ChatHub.cs b/src/Components/test/testassets/TestServer/ChatHub.cs
index 770f2dcaa9f4..eaef0eb927a3 100644
--- a/src/Components/test/testassets/TestServer/ChatHub.cs
+++ b/src/Components/test/testassets/TestServer/ChatHub.cs
@@ -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;
@@ -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();
+ }
}
}