Fix User Agent on .NET Client (#13298)

This commit is contained in:
Mikael Mengistu 2019-08-22 16:45:05 -05:00 committed by GitHub
parent e0f95cfa6f
commit 5c05b9288a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 188 additions and 13 deletions

View File

@ -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]
[WebSocketsSupportedCondition]
public async Task WebSocketOptionsAreApplied()

View File

@ -3,6 +3,7 @@
using System;
using System.IO.Pipelines;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Reflection;
@ -113,16 +114,17 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
testHttpHandler.OnRequest(async (request, next, token) =>
{
var userAgentHeaderCollection = request.Headers.UserAgent;
var userAgentHeader = Assert.Single(userAgentHeaderCollection);
Assert.Equal("Microsoft.AspNetCore.Http.Connections.Client", userAgentHeader.Product.Name);
var userAgentHeader = request.Headers.UserAgent.ToString();
Assert.NotNull(userAgentHeader);
Assert.StartsWith("Microsoft SignalR/", userAgentHeader);
// user agent version should come from version embedded in assembly metadata
var assemblyVersion = typeof(Constants)
.Assembly
.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
Assert.Equal(assemblyVersion.InformationalVersion, userAgentHeader.Product.Version);
Assert.Contains(assemblyVersion.InformationalVersion, userAgentHeader);
requestsExecuted = true;

View File

@ -551,14 +551,34 @@ namespace Microsoft.AspNetCore.Http.Connections.Client
httpClient.Timeout = HttpClientTimeout;
// 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
if (_httpConnectionOptions?.Headers != null)
{
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);
}
}
}

View File

@ -3,19 +3,18 @@
using System.Diagnostics;
using System.Linq;
using System.Net.Http.Headers;
using System.Reflection;
using System.Runtime.InteropServices;
namespace Microsoft.AspNetCore.Http.Connections.Client.Internal
{
internal static class Constants
{
public static readonly ProductInfoHeaderValue UserAgentHeader;
public const string UserAgent = "User-Agent";
public static readonly string UserAgentHeader;
static Constants()
{
var userAgent = "Microsoft.AspNetCore.Http.Connections.Client";
var assemblyVersion = typeof(Constants)
.Assembly
.GetCustomAttributes<AssemblyInformationalVersionAttribute>()
@ -23,14 +22,22 @@ namespace Microsoft.AspNetCore.Http.Connections.Client.Internal
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
// but in case it isn't then don't include version in user-agent
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);
}
}
}

View File

@ -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);
}
}
}