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.
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.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"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in New Issue