Client sends user agent with version based on assembly version (#1551)
This commit is contained in:
parent
1c44e8febf
commit
a41bf6228f
|
|
@ -1,13 +1,36 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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.Net.Http.Headers;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Sockets.Client.Http
|
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;
|
||||||
public static readonly ProductInfoHeaderValue UserAgentHeader = ProductInfoHeaderValue.Parse(UserAgent);
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,9 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
{
|
{
|
||||||
_webSocket = new ClientWebSocket();
|
_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)
|
if (httpOptions?.Headers != null)
|
||||||
{
|
{
|
||||||
foreach (var header in httpOptions.Headers)
|
foreach (var header in httpOptions.Headers)
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,11 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
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.Net.Http.Headers;
|
||||||
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Channels;
|
using System.Threading.Channels;
|
||||||
|
|
@ -13,6 +16,7 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.SignalR.Client.Tests;
|
using Microsoft.AspNetCore.SignalR.Client.Tests;
|
||||||
using Microsoft.AspNetCore.Sockets;
|
using Microsoft.AspNetCore.Sockets;
|
||||||
using Microsoft.AspNetCore.Sockets.Client;
|
using Microsoft.AspNetCore.Sockets.Client;
|
||||||
|
using Microsoft.AspNetCore.Sockets.Client.Http;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Moq.Protected;
|
using Moq.Protected;
|
||||||
using Xunit;
|
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]
|
[Fact]
|
||||||
public async Task LongPollingTransportThrowsForInvalidTransferMode()
|
public async Task LongPollingTransportThrowsForInvalidTransferMode()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,10 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Pipelines;
|
using System.IO.Pipelines;
|
||||||
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Channels;
|
using System.Threading.Channels;
|
||||||
|
|
@ -13,6 +15,7 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Client.Tests;
|
using Microsoft.AspNetCore.Client.Tests;
|
||||||
using Microsoft.AspNetCore.Sockets;
|
using Microsoft.AspNetCore.Sockets;
|
||||||
using Microsoft.AspNetCore.Sockets.Client;
|
using Microsoft.AspNetCore.Sockets.Client;
|
||||||
|
using Microsoft.AspNetCore.Sockets.Client.Http;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Moq.Protected;
|
using Moq.Protected;
|
||||||
using Xunit;
|
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]
|
[Fact]
|
||||||
public async Task SSETransportThrowsForInvalidTransferMode()
|
public async Task SSETransportThrowsForInvalidTransferMode()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
{
|
{
|
||||||
public class EchoEndPoint : EndPoint
|
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 result = await connection.Transport.Input.ReadAsync();
|
||||||
var buffer = result.Buffer;
|
var buffer = result.Buffer;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,11 +14,13 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
services.AddSockets();
|
services.AddSockets();
|
||||||
services.AddSignalR();
|
services.AddSignalR();
|
||||||
services.AddEndPoint<EchoEndPoint>();
|
services.AddEndPoint<EchoEndPoint>();
|
||||||
|
services.AddEndPoint<HttpHeaderEndPoint>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
||||||
{
|
{
|
||||||
app.UseSockets(options => options.MapEndPoint<EchoEndPoint>("/echo"));
|
app.UseSockets(options => options.MapEndPoint<EchoEndPoint>("/echo"));
|
||||||
|
app.UseSockets(options => options.MapEndPoint<HttpHeaderEndPoint>("/httpheader"));
|
||||||
app.UseSignalR(options => options.MapHub<UncreatableHub>("/uncreatable"));
|
app.UseSignalR(options => options.MapHub<UncreatableHub>("/uncreatable"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,17 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.IO.Pipelines;
|
using System.IO.Pipelines;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Sockets;
|
using Microsoft.AspNetCore.Sockets;
|
||||||
using Microsoft.AspNetCore.Sockets.Client;
|
using Microsoft.AspNetCore.Sockets.Client;
|
||||||
|
using Microsoft.AspNetCore.Sockets.Client.Http;
|
||||||
using Microsoft.AspNetCore.Testing.xunit;
|
using Microsoft.AspNetCore.Testing.xunit;
|
||||||
using Microsoft.Extensions.Logging.Testing;
|
using Microsoft.Extensions.Logging.Testing;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Xunit.Abstractions;
|
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]
|
[ConditionalFact]
|
||||||
[OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, WindowsVersions.Win2008R2, SkipReason = "No WebSockets Client for this platform")]
|
[OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, WindowsVersions.Win2008R2, SkipReason = "No WebSockets Client for this platform")]
|
||||||
public async Task WebSocketsTransportStopsWhenConnectionChannelClosed()
|
public async Task WebSocketsTransportStopsWhenConnectionChannelClosed()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue