diff --git a/build/dependencies.props b/build/dependencies.props index f94daabf3..0084856a8 100644 --- a/build/dependencies.props +++ b/build/dependencies.props @@ -5,8 +5,8 @@ 1.7.3.4 - 1.1.0 - 1.1.0 + 1.0.0 + 1.0.0 3.19.1 2.3.2 4.5.0 @@ -19,7 +19,7 @@ 2.4.0 - 2.2.0 + 2.1.0 2.1.0 4.5.0 @@ -40,7 +40,7 @@ 1.0.0 - 1.1.0 + 1.0.0 15.6.1 4.7.49 2.4.0 diff --git a/src/Microsoft.Azure.SignalR.AspNet/TraceManagerLoggerProvider.cs b/src/Microsoft.Azure.SignalR.AspNet/TraceManagerLoggerProvider.cs index 87359710f..2c6056ab2 100644 --- a/src/Microsoft.Azure.SignalR.AspNet/TraceManagerLoggerProvider.cs +++ b/src/Microsoft.Azure.SignalR.AspNet/TraceManagerLoggerProvider.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; +using System.Diagnostics; using Microsoft.AspNet.SignalR.Tracing; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.TraceSource; @@ -19,11 +21,39 @@ public TraceManagerLoggerProvider(ITraceManager traceManager) public ILogger CreateLogger(string categoryName) { var traceSource = _traceManager[categoryName]; - return new TraceSourceLogger(traceSource); + return new InternalTraceSourceLogger(traceSource); } public void Dispose() { } + + /// + /// Use an InternalTraceSourceLogger to get rid of LogicalOperationStack inside the TraceSourceScope + /// + private class InternalTraceSourceLogger : ILogger + { + private readonly ILogger _inner; + + public InternalTraceSourceLogger(TraceSource traceSource) + { + _inner = new TraceSourceLogger(traceSource); + } + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + _inner.Log(logLevel, eventId, state, exception, formatter); + } + + public bool IsEnabled(LogLevel logLevel) + { + return _inner.IsEnabled(logLevel); + } + + public IDisposable BeginScope(TState state) + { + return null; + } + } } } \ No newline at end of file diff --git a/src/Microsoft.Azure.SignalR.Common/ServiceConnections/ServiceConnectionBase.cs b/src/Microsoft.Azure.SignalR.Common/ServiceConnections/ServiceConnectionBase.cs index 3166eb17a..82433bbb1 100644 --- a/src/Microsoft.Azure.SignalR.Common/ServiceConnections/ServiceConnectionBase.cs +++ b/src/Microsoft.Azure.SignalR.Common/ServiceConnections/ServiceConnectionBase.cs @@ -308,7 +308,16 @@ private async Task ReceiveHandshakeResponseAsync(PipeReader input, Cancell } // Handshake error. Will stop reconnect. - Log.HandshakeError(_logger, handshakeResponse.ErrorMessage); + if (_connectionType == ServerConnectionType.OnDemand) + { + // Handshake errors on on-demand connections are acceptable. + Log.OnDemandConnectionHandshakeResponse(_logger, handshakeResponse.ErrorMessage); + } + else + { + Log.HandshakeError(_logger, handshakeResponse.ErrorMessage); + } + return false; } } @@ -603,6 +612,9 @@ private static class Log private static readonly Action _unexpectedExceptionInStop = LoggerMessage.Define(LogLevel.Warning, new EventId(29, "UnexpectedExceptionInStop"), "Connection {ServiceConnectionId} got unexpected exception in StopAsync."); + private static readonly Action _onDemandConnectionHandshakeResponse = + LoggerMessage.Define(LogLevel.Information, new EventId(30, "OnDemandConnectionHandshakeResponse"), "Service returned handshake response: {Message}"); + public static void FailedToWrite(ILogger logger, Exception exception) { _failedToWrite(logger, exception); @@ -730,6 +742,11 @@ public static void HandshakeError(ILogger logger, string error) _handshakeError(logger, error, null); } + public static void OnDemandConnectionHandshakeResponse(ILogger logger, string message) + { + _onDemandConnectionHandshakeResponse(logger, message, null); + } + public static void SentPing(ILogger logger) { _sentPing(logger, null); diff --git a/test/Microsoft.Azure.SignalR.AspNet.Tests/Infrastructure/TestServiceMessageHandler.cs b/test/Microsoft.Azure.SignalR.AspNet.Tests/Infrastructure/TestServiceMessageHandler.cs index 86d726696..5722d0ead 100644 --- a/test/Microsoft.Azure.SignalR.AspNet.Tests/Infrastructure/TestServiceMessageHandler.cs +++ b/test/Microsoft.Azure.SignalR.AspNet.Tests/Infrastructure/TestServiceMessageHandler.cs @@ -23,8 +23,15 @@ public TestServiceConnectionManager(string appName, IReadOnlyList hubs) public override Task WriteAsync(ServiceMessage serviceMessage) { - var tcs = _waitForTransportOutputMessage.GetOrAdd(serviceMessage.GetType(), i => new TaskCompletionSource()); - tcs.TrySetResult(serviceMessage); + if (_waitForTransportOutputMessage.TryGetValue(serviceMessage.GetType(), out var tcs)) + { + tcs.SetResult(serviceMessage); + } + else + { + throw new InvalidOperationException("Not expected to write before tcs is inited"); + } + return Task.CompletedTask; } @@ -44,7 +51,13 @@ public override Task WriteAsync(string partitionKey, ServiceMessage serviceMessa public Task WaitForTransportOutputMessageAsync(Type messageType) { - var tcs = _waitForTransportOutputMessage[messageType] = new TaskCompletionSource(); + if (_waitForTransportOutputMessage.TryGetValue(messageType, out var tcs)) + { + tcs.TrySetCanceled(); + } + + // re-init the tcs + tcs = _waitForTransportOutputMessage[messageType] = new TaskCompletionSource(); return tcs.Task; } diff --git a/test/Microsoft.Azure.SignalR.AspNet.Tests/ServiceConnectionTests.cs b/test/Microsoft.Azure.SignalR.AspNet.Tests/ServiceConnectionTests.cs index e164db903..547d8d01b 100644 --- a/test/Microsoft.Azure.SignalR.AspNet.Tests/ServiceConnectionTests.cs +++ b/test/Microsoft.Azure.SignalR.AspNet.Tests/ServiceConnectionTests.cs @@ -47,18 +47,21 @@ public async Task ServiceConnectionDispatchTest() // Application layer sends OpenConnectionMessage var openConnectionMessage = new OpenConnectionMessage(clientConnection, new Claim[0], null, "?transport=webSockets"); + var task = proxy.WaitForClientConnectAsync(clientConnection).OrTimeout(); await proxy.WriteMessageAsync(openConnectionMessage); - await proxy.WaitForClientConnectAsync(clientConnection).OrTimeout(); + await task; while (count < 1000) { + task = proxy.WaitForApplicationMessageAsync(clientConnection).OrTimeout(); await proxy.WriteMessageAsync(new ConnectionDataMessage(clientConnection, GetPayload("Hello World"))); - await proxy.WaitForApplicationMessageAsync(clientConnection).OrTimeout(); + await task; count++; } + task = proxy.WaitForClientDisconnectAsync(clientConnection).OrTimeout(); await proxy.WriteMessageAsync(new CloseConnectionMessage(clientConnection)); - await proxy.WaitForClientDisconnectAsync(clientConnection).OrTimeout(); + await task; // Validate in transport for 1000 data messages. _clientConnectionManager.CurrentTransports.TryGetValue(clientConnection, out var transport); @@ -113,8 +116,9 @@ public async Task ServiceConnectionDispatchGroupMessagesTest() await lgTask; await gbTask; + var dTask = proxy.WaitForClientDisconnectAsync(clientConnection).OrTimeout(); await proxy.WriteMessageAsync(new CloseConnectionMessage(clientConnection)); - await proxy.WaitForClientDisconnectAsync(clientConnection).OrTimeout(); + await dTask; } } } diff --git a/test/Microsoft.Azure.SignalR.AspNet.Tests/TraceManagerLoggerProviderTest.cs b/test/Microsoft.Azure.SignalR.AspNet.Tests/TraceManagerLoggerProviderTest.cs new file mode 100644 index 000000000..78875f3b9 --- /dev/null +++ b/test/Microsoft.Azure.SignalR.AspNet.Tests/TraceManagerLoggerProviderTest.cs @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Configuration; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNet.SignalR; +using Microsoft.AspNet.SignalR.Hubs; +using Microsoft.AspNet.SignalR.Json; +using Microsoft.AspNet.SignalR.Messaging; +using Microsoft.AspNet.SignalR.Tracing; +using Microsoft.AspNet.SignalR.Transports; +using Microsoft.AspNetCore.Connections; +using Microsoft.AspNetCore.Http.Connections.Client; +using Microsoft.Azure.SignalR.Protocol; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Microsoft.Owin.Hosting; +using Newtonsoft.Json; +using Owin; +using Xunit; + +namespace Microsoft.Azure.SignalR.AspNet.Tests +{ + public class TraceManagerLoggerProviderTest + { + /// + /// TraceManagerLoggerProvider throws when its CreateLogger returns TraceSourceLogger when using HttpConnections.Client 1.0.0 + /// + /// + [Fact] + public async Task TestTraceManagerLoggerProviderCanDisposeHttpConnection() + { + var lf = new LoggerFactory(); + lf.AddProvider(new TraceManagerLoggerProvider(new TraceManager())); + await StartAsync(lf); + } + + private static async Task StartAsync(ILoggerFactory lf) + { + var connection = await ConnectAsync(lf); + // var connection = Connect(lf); + await ((HttpConnection)connection).DisposeAsync(); + } + + public static async Task ConnectAsync(ILoggerFactory lf) + { + // Await to enforce it run in another thread + await Task.Yield(); + var httpConnectionOptions = new HttpConnectionOptions + { + Url = new Uri("http://locolhost"), + }; + + return new HttpConnection(httpConnectionOptions, lf); + } + } +} \ No newline at end of file