Fix User Agent on .NET Client (#13298)
This commit is contained in:
parent
e0f95cfa6f
commit
5c05b9288a
|
|
@ -1414,6 +1414,118 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UserAgentIsSet()
|
||||||
|
{
|
||||||
|
using (StartServer<Startup>(out var server))
|
||||||
|
{
|
||||||
|
var hubConnection = new HubConnectionBuilder()
|
||||||
|
.WithLoggerFactory(LoggerFactory)
|
||||||
|
.WithUrl(server.Url + "/default", HttpTransportType.LongPolling, options =>
|
||||||
|
{
|
||||||
|
options.Headers["X-test"] = "42";
|
||||||
|
options.Headers["X-42"] = "test";
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await hubConnection.StartAsync().OrTimeout();
|
||||||
|
var headerValues = await hubConnection.InvokeAsync<string[]>(nameof(TestHub.GetHeaderValues), new[] { "User-Agent" }).OrTimeout();
|
||||||
|
Assert.NotNull(headerValues);
|
||||||
|
Assert.Single(headerValues);
|
||||||
|
|
||||||
|
var userAgent = headerValues[0];
|
||||||
|
|
||||||
|
Assert.StartsWith("Microsoft SignalR/", userAgent);
|
||||||
|
|
||||||
|
var majorVersion = typeof(HttpConnection).Assembly.GetName().Version.Major;
|
||||||
|
var minorVersion = typeof(HttpConnection).Assembly.GetName().Version.Minor;
|
||||||
|
|
||||||
|
Assert.Contains($"{majorVersion}.{minorVersion}", userAgent);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LoggerFactory.CreateLogger<HubConnectionTests>().LogError(ex, "{ExceptionType} from test", ex.GetType().FullName);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
await hubConnection.DisposeAsync().OrTimeout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UserAgentCanBeCleared()
|
||||||
|
{
|
||||||
|
using (StartServer<Startup>(out var server))
|
||||||
|
{
|
||||||
|
var hubConnection = new HubConnectionBuilder()
|
||||||
|
.WithLoggerFactory(LoggerFactory)
|
||||||
|
.WithUrl(server.Url + "/default", HttpTransportType.LongPolling, options =>
|
||||||
|
{
|
||||||
|
options.Headers["User-Agent"] = "";
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await hubConnection.StartAsync().OrTimeout();
|
||||||
|
var headerValues = await hubConnection.InvokeAsync<string[]>(nameof(TestHub.GetHeaderValues), new[] { "User-Agent" }).OrTimeout();
|
||||||
|
Assert.NotNull(headerValues);
|
||||||
|
Assert.Single(headerValues);
|
||||||
|
|
||||||
|
var userAgent = headerValues[0];
|
||||||
|
|
||||||
|
Assert.Null(userAgent);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LoggerFactory.CreateLogger<HubConnectionTests>().LogError(ex, "{ExceptionType} from test", ex.GetType().FullName);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
await hubConnection.DisposeAsync().OrTimeout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UserAgentCanBeSet()
|
||||||
|
{
|
||||||
|
using (StartServer<Startup>(out var server))
|
||||||
|
{
|
||||||
|
var hubConnection = new HubConnectionBuilder()
|
||||||
|
.WithLoggerFactory(LoggerFactory)
|
||||||
|
.WithUrl(server.Url + "/default", HttpTransportType.LongPolling, options =>
|
||||||
|
{
|
||||||
|
options.Headers["User-Agent"] = "User Value";
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await hubConnection.StartAsync().OrTimeout();
|
||||||
|
var headerValues = await hubConnection.InvokeAsync<string[]>(nameof(TestHub.GetHeaderValues), new[] { "User-Agent" }).OrTimeout();
|
||||||
|
Assert.NotNull(headerValues);
|
||||||
|
Assert.Single(headerValues);
|
||||||
|
|
||||||
|
var userAgent = headerValues[0];
|
||||||
|
|
||||||
|
Assert.Equal("User Value", userAgent);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LoggerFactory.CreateLogger<HubConnectionTests>().LogError(ex, "{ExceptionType} from test", ex.GetType().FullName);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
await hubConnection.DisposeAsync().OrTimeout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[ConditionalFact]
|
[ConditionalFact]
|
||||||
[WebSocketsSupportedCondition]
|
[WebSocketsSupportedCondition]
|
||||||
public async Task WebSocketOptionsAreApplied()
|
public async Task WebSocketOptionsAreApplied()
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO.Pipelines;
|
using System.IO.Pipelines;
|
||||||
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
@ -113,16 +114,17 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
|
|
||||||
testHttpHandler.OnRequest(async (request, next, token) =>
|
testHttpHandler.OnRequest(async (request, next, token) =>
|
||||||
{
|
{
|
||||||
var userAgentHeaderCollection = request.Headers.UserAgent;
|
var userAgentHeader = request.Headers.UserAgent.ToString();
|
||||||
var userAgentHeader = Assert.Single(userAgentHeaderCollection);
|
|
||||||
Assert.Equal("Microsoft.AspNetCore.Http.Connections.Client", userAgentHeader.Product.Name);
|
Assert.NotNull(userAgentHeader);
|
||||||
|
Assert.StartsWith("Microsoft SignalR/", userAgentHeader);
|
||||||
|
|
||||||
// user agent version should come from version embedded in assembly metadata
|
// user agent version should come from version embedded in assembly metadata
|
||||||
var assemblyVersion = typeof(Constants)
|
var assemblyVersion = typeof(Constants)
|
||||||
.Assembly
|
.Assembly
|
||||||
.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
|
.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
|
||||||
|
|
||||||
Assert.Equal(assemblyVersion.InformationalVersion, userAgentHeader.Product.Version);
|
Assert.Contains(assemblyVersion.InformationalVersion, userAgentHeader);
|
||||||
|
|
||||||
requestsExecuted = true;
|
requestsExecuted = true;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -551,14 +551,34 @@ namespace Microsoft.AspNetCore.Http.Connections.Client
|
||||||
httpClient.Timeout = HttpClientTimeout;
|
httpClient.Timeout = HttpClientTimeout;
|
||||||
|
|
||||||
// Start with the user agent header
|
// Start with the user agent header
|
||||||
httpClient.DefaultRequestHeaders.UserAgent.Add(Constants.UserAgentHeader);
|
httpClient.DefaultRequestHeaders.Add(Constants.UserAgent, Constants.UserAgentHeader);
|
||||||
|
|
||||||
// Apply any headers configured on the HttpConnectionOptions
|
// Apply any headers configured on the HttpConnectionOptions
|
||||||
if (_httpConnectionOptions?.Headers != null)
|
if (_httpConnectionOptions?.Headers != null)
|
||||||
{
|
{
|
||||||
foreach (var header in _httpConnectionOptions.Headers)
|
foreach (var header in _httpConnectionOptions.Headers)
|
||||||
{
|
{
|
||||||
httpClient.DefaultRequestHeaders.Add(header.Key, header.Value);
|
// 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))
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(header.Value))
|
||||||
|
{
|
||||||
|
httpClient.DefaultRequestHeaders.Remove(header.Key);
|
||||||
|
}
|
||||||
|
else if (httpClient.DefaultRequestHeaders.Contains(header.Key))
|
||||||
|
{
|
||||||
|
httpClient.DefaultRequestHeaders.Remove(header.Key);
|
||||||
|
httpClient.DefaultRequestHeaders.Add(header.Key, header.Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
httpClient.DefaultRequestHeaders.Add(header.Key, header.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
httpClient.DefaultRequestHeaders.Add(header.Key, header.Value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,19 +3,18 @@
|
||||||
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http.Headers;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Http.Connections.Client.Internal
|
namespace Microsoft.AspNetCore.Http.Connections.Client.Internal
|
||||||
{
|
{
|
||||||
internal static class Constants
|
internal static class Constants
|
||||||
{
|
{
|
||||||
public static readonly ProductInfoHeaderValue UserAgentHeader;
|
public const string UserAgent = "User-Agent";
|
||||||
|
public static readonly string UserAgentHeader;
|
||||||
|
|
||||||
static Constants()
|
static Constants()
|
||||||
{
|
{
|
||||||
var userAgent = "Microsoft.AspNetCore.Http.Connections.Client";
|
|
||||||
|
|
||||||
var assemblyVersion = typeof(Constants)
|
var assemblyVersion = typeof(Constants)
|
||||||
.Assembly
|
.Assembly
|
||||||
.GetCustomAttributes<AssemblyInformationalVersionAttribute>()
|
.GetCustomAttributes<AssemblyInformationalVersionAttribute>()
|
||||||
|
|
@ -23,14 +22,22 @@ namespace Microsoft.AspNetCore.Http.Connections.Client.Internal
|
||||||
|
|
||||||
Debug.Assert(assemblyVersion != null);
|
Debug.Assert(assemblyVersion != null);
|
||||||
|
|
||||||
|
var majorVersion = typeof(Constants).Assembly.GetName().Version.Major;
|
||||||
|
var minorVersion = typeof(Constants).Assembly.GetName().Version.Minor;
|
||||||
|
var os = RuntimeInformation.OSDescription;
|
||||||
|
var runtime = ".NET";
|
||||||
|
var runtimeVersion = RuntimeInformation.FrameworkDescription;
|
||||||
|
|
||||||
// assembly version attribute should always be present
|
// assembly version attribute should always be present
|
||||||
// but in case it isn't then don't include version in user-agent
|
// but in case it isn't then don't include version in user-agent
|
||||||
if (assemblyVersion != null)
|
if (assemblyVersion != null)
|
||||||
{
|
{
|
||||||
userAgent += "/" + assemblyVersion.InformationalVersion;
|
UserAgentHeader = $"Microsoft SignalR/{majorVersion}.{minorVersion} ({assemblyVersion.InformationalVersion}; {os}; {runtime}; {runtimeVersion})";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UserAgentHeader = $"Microsoft SignalR/{majorVersion}.{minorVersion} ({os}; {runtime}; {runtimeVersion})";
|
||||||
}
|
}
|
||||||
|
|
||||||
UserAgentHeader = ProductInfoHeaderValue.Parse(userAgent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
// 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.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Microsoft.AspNetCore.Http.Connections.Client;
|
||||||
|
using Xunit;
|
||||||
|
using Constants = Microsoft.AspNetCore.Http.Connections.Client.Internal.Constants;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
|
{
|
||||||
|
public class UserAgentHeaderTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void UserAgentHeaderIsAccurate()
|
||||||
|
{
|
||||||
|
var majorVersion = typeof(HttpConnection).Assembly.GetName().Version.Major;
|
||||||
|
var minorVersion = typeof(HttpConnection).Assembly.GetName().Version.Minor;
|
||||||
|
var version = typeof(HttpConnection).Assembly.GetName().Version;
|
||||||
|
var os = RuntimeInformation.OSDescription;
|
||||||
|
var runtime = ".NET";
|
||||||
|
var runtimeVersion = RuntimeInformation.FrameworkDescription;
|
||||||
|
var assemblyVersion = typeof(Constants)
|
||||||
|
.Assembly
|
||||||
|
.GetCustomAttributes<AssemblyInformationalVersionAttribute>()
|
||||||
|
.FirstOrDefault();
|
||||||
|
var userAgent = Constants.UserAgentHeader;
|
||||||
|
var expectedUserAgent = $"Microsoft SignalR/{majorVersion}.{minorVersion} ({assemblyVersion.InformationalVersion}; {os}; {runtime}; {runtimeVersion})";
|
||||||
|
|
||||||
|
Assert.Equal(expectedUserAgent, userAgent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue