Client sends user agent with version based on assembly version (#1551)

This commit is contained in:
James Newton-King 2018-03-08 10:25:12 +13:00 committed by GitHub
parent 1c44e8febf
commit a41bf6228f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 190 additions and 4 deletions

View File

@ -1,13 +1,36 @@
// 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.Diagnostics;
using System.Linq;
using System.Net.Http.Headers;
using System.Reflection;
namespace Microsoft.AspNetCore.Sockets.Client.Http
{
public class Constants
public static class Constants
{
private static readonly string UserAgent = "Microsoft.AspNetCore.Sockets.Client.Http/1.0.0-alpha";
public static readonly ProductInfoHeaderValue UserAgentHeader = ProductInfoHeaderValue.Parse(UserAgent);
public static readonly ProductInfoHeaderValue UserAgentHeader;
static Constants()
{
var userAgent = "Microsoft.AspNetCore.Sockets.Client.Http";
var assemblyVersion = typeof(Constants)
.Assembly
.GetCustomAttributes<AssemblyInformationalVersionAttribute>()
.FirstOrDefault();
Debug.Assert(assemblyVersion != null);
// 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 = ProductInfoHeaderValue.Parse(userAgent);
}
}
}

View File

@ -34,6 +34,9 @@ namespace Microsoft.AspNetCore.Sockets.Client
{
_webSocket = new ClientWebSocket();
// Issue in ClientWebSocket prevents user-agent being set - https://github.com/dotnet/corefx/issues/26627
//_webSocket.Options.SetRequestHeader("User-Agent", Constants.UserAgentHeader.ToString());
if (httpOptions?.Headers != null)
{
foreach (var header in httpOptions.Headers)

View File

@ -4,8 +4,11 @@
using System;
using System.Collections.Generic;
using System.IO.Pipelines;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Channels;
@ -13,6 +16,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR.Client.Tests;
using Microsoft.AspNetCore.Sockets;
using Microsoft.AspNetCore.Sockets.Client;
using Microsoft.AspNetCore.Sockets.Client.Http;
using Moq;
using Moq.Protected;
using Xunit;
@ -388,6 +392,50 @@ namespace Microsoft.AspNetCore.Client.Tests
}
}
[Fact]
public async Task LongPollingTransportSetsUserAgent()
{
HttpHeaderValueCollection<ProductInfoHeaderValue> userAgentHeaderCollection = null;
var mockHttpHandler = new Mock<HttpMessageHandler>();
mockHttpHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.Returns<HttpRequestMessage, CancellationToken>(async (request, cancellationToken) =>
{
userAgentHeaderCollection = request.Headers.UserAgent;
await Task.Yield();
return ResponseUtils.CreateResponse(HttpStatusCode.OK);
});
using (var httpClient = new HttpClient(mockHttpHandler.Object))
{
var longPollingTransport = new LongPollingTransport(httpClient);
try
{
var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);
Assert.Null(longPollingTransport.Mode);
await longPollingTransport.StartAsync(new Uri("http://fakeuri.org"), pair.Application, TransferMode.Text, connection: new TestConnection());
}
finally
{
await longPollingTransport.StopAsync();
}
}
Assert.NotNull(userAgentHeaderCollection);
var userAgentHeader = Assert.Single(userAgentHeaderCollection);
Assert.Equal("Microsoft.AspNetCore.Sockets.Client.Http", userAgentHeader.Product.Name);
// 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);
}
[Fact]
public async Task LongPollingTransportThrowsForInvalidTransferMode()
{

View File

@ -4,8 +4,10 @@
using System;
using System.IO;
using System.IO.Pipelines;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Channels;
@ -13,6 +15,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Client.Tests;
using Microsoft.AspNetCore.Sockets;
using Microsoft.AspNetCore.Sockets.Client;
using Microsoft.AspNetCore.Sockets.Client.Http;
using Moq;
using Moq.Protected;
using Xunit;
@ -299,6 +302,42 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
}
}
[Fact]
public async Task SSETransportSetsUserAgent()
{
HttpHeaderValueCollection<ProductInfoHeaderValue> userAgentHeaderCollection = null;
var mockHttpHandler = new Mock<HttpMessageHandler>();
mockHttpHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.Returns<HttpRequestMessage, CancellationToken>(async (request, cancellationToken) =>
{
userAgentHeaderCollection = request.Headers.UserAgent;
await Task.Yield();
return new HttpResponseMessage { Content = new StringContent(string.Empty) };
});
using (var httpClient = new HttpClient(mockHttpHandler.Object))
{
var sseTransport = new ServerSentEventsTransport(httpClient);
var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);
await sseTransport.StartAsync(new Uri("http://fakeuri.org"), pair.Application, TransferMode.Text, connection: Mock.Of<IConnection>()).OrTimeout();
await sseTransport.StopAsync().OrTimeout();
}
Assert.NotNull(userAgentHeaderCollection);
var userAgentHeader = Assert.Single(userAgentHeaderCollection);
Assert.Equal("Microsoft.AspNetCore.Sockets.Client.Http", userAgentHeader.Product.Name);
// 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);
}
[Fact]
public async Task SSETransportThrowsForInvalidTransferMode()
{

View File

@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
{
public class EchoEndPoint : EndPoint
{
public async override Task OnConnectedAsync(ConnectionContext connection)
public override async Task OnConnectedAsync(ConnectionContext connection)
{
var result = await connection.Transport.Input.ReadAsync();
var buffer = result.Buffer;

View File

@ -0,0 +1,37 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Protocols;
using Microsoft.AspNetCore.Sockets;
using Microsoft.AspNetCore.Sockets.Http.Features;
namespace Microsoft.AspNetCore.SignalR.Tests
{
public class HttpHeaderEndPoint : EndPoint
{
public override async Task OnConnectedAsync(ConnectionContext connection)
{
var result = await connection.Transport.Input.ReadAsync();
var buffer = result.Buffer;
try
{
var headers = connection.Features.Get<IHttpContextFeature>().HttpContext.Request.Headers;
var headerName = Encoding.UTF8.GetString(buffer.ToArray());
var headerValues = headers.FirstOrDefault(h => string.Equals(h.Key, headerName, StringComparison.OrdinalIgnoreCase)).Value.ToArray();
var data = Encoding.UTF8.GetBytes(string.Join(",", headerValues));
await connection.Transport.Output.WriteAsync(data);
}
finally
{
connection.Transport.Input.AdvanceTo(buffer.End);
}
}
}
}

View File

@ -14,11 +14,13 @@ namespace Microsoft.AspNetCore.SignalR.Tests
services.AddSockets();
services.AddSignalR();
services.AddEndPoint<EchoEndPoint>();
services.AddEndPoint<HttpHeaderEndPoint>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseSockets(options => options.MapEndPoint<EchoEndPoint>("/echo"));
app.UseSockets(options => options.MapEndPoint<HttpHeaderEndPoint>("/httpheader"));
app.UseSignalR(options => options.MapHub<UncreatableHub>("/uncreatable"));
}
}

View File

@ -4,12 +4,17 @@
using System;
using System.Buffers;
using System.IO.Pipelines;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Sockets;
using Microsoft.AspNetCore.Sockets.Client;
using Microsoft.AspNetCore.Sockets.Client.Http;
using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Extensions.Logging.Testing;
using Moq;
using Newtonsoft.Json.Linq;
using Xunit;
using Xunit.Abstractions;
@ -45,6 +50,35 @@ namespace Microsoft.AspNetCore.SignalR.Tests
}
}
[ConditionalFact(Skip = "Issue in ClientWebSocket prevents user-agent being set - https://github.com/dotnet/corefx/issues/26627")]
[OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, WindowsVersions.Win2008R2, SkipReason = "No WebSockets Client for this platform")]
public async Task WebSocketsTransportSendsUserAgent()
{
using (StartLog(out var loggerFactory))
{
var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);
var webSocketsTransport = new WebSocketsTransport(httpOptions: null, loggerFactory: loggerFactory);
await webSocketsTransport.StartAsync(new Uri(_serverFixture.WebSocketsUrl + "/httpheader"), pair.Application,
TransferMode.Binary, connection: Mock.Of<IConnection>()).OrTimeout();
await pair.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("User-Agent"));
// The HTTP header endpoint closes the connection immediately after sending response which should stop the transport
await webSocketsTransport.Running.OrTimeout();
Assert.True(pair.Transport.Input.TryRead(out var result));
string userAgent = Encoding.UTF8.GetString(result.Buffer.ToArray());
// user agent version should come from version embedded in assembly metadata
var assemblyVersion = typeof(Constants)
.Assembly
.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
Assert.Equal("Microsoft.AspNetCore.Sockets.Client.Http/" + assemblyVersion.InformationalVersion, userAgent);
}
}
[ConditionalFact]
[OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, WindowsVersions.Win2008R2, SkipReason = "No WebSockets Client for this platform")]
public async Task WebSocketsTransportStopsWhenConnectionChannelClosed()