diff --git a/src/Microsoft.AspNetCore.Sockets.Http/Internal/SocketLoggerExtensions.cs b/src/Microsoft.AspNetCore.Sockets.Http/Internal/SocketLoggerExtensions.cs index 19b604f467..af419fd865 100644 --- a/src/Microsoft.AspNetCore.Sockets.Http/Internal/SocketLoggerExtensions.cs +++ b/src/Microsoft.AspNetCore.Sockets.Http/Internal/SocketLoggerExtensions.cs @@ -90,6 +90,9 @@ namespace Microsoft.AspNetCore.Sockets.Internal private static readonly Action _errorWritingFrame = LoggerMessage.Define(LogLevel.Error, 11, "{time}: Connection Id {connectionId}: Error writing frame."); + private static readonly Action _sendFailed = + LoggerMessage.Define(LogLevel.Trace, 12, "{time}: Connection Id {connectionId}: Socket failed to send."); + // Category: ServerSentEventsTransport private static readonly Action _sseWritingMessage = LoggerMessage.Define(LogLevel.Debug, 0, "{time}: Connection Id {connectionId}: Writing a {count} byte message."); @@ -302,6 +305,14 @@ namespace Microsoft.AspNetCore.Sockets.Internal } } + public static void SendFailed(this ILogger logger, string connectionId, Exception ex) + { + if (logger.IsEnabled(LogLevel.Trace)) + { + _sendFailed(logger, DateTime.Now, connectionId, ex); + } + } + public static void SSEWritingMessage(this ILogger logger, string connectionId, int count) { if (logger.IsEnabled(LogLevel.Debug)) diff --git a/src/Microsoft.AspNetCore.Sockets.Http/Internal/Transports/WebSocketsTransport.cs b/src/Microsoft.AspNetCore.Sockets.Http/Internal/Transports/WebSocketsTransport.cs index bc41807e85..5811dc6fd4 100644 --- a/src/Microsoft.AspNetCore.Sockets.Http/Internal/Transports/WebSocketsTransport.cs +++ b/src/Microsoft.AspNetCore.Sockets.Http/Internal/Transports/WebSocketsTransport.cs @@ -178,7 +178,16 @@ namespace Microsoft.AspNetCore.Sockets.Internal.Transports { _logger.SendPayload(_connectionId, buffer.Length); - await ws.SendAsync(new ArraySegment(buffer), _options.WebSocketMessageType, endOfMessage: true, cancellationToken: CancellationToken.None); + if (WebSocketCanSend(ws)) + { + await ws.SendAsync(new ArraySegment(buffer), _options.WebSocketMessageType, endOfMessage: true, cancellationToken: CancellationToken.None); + } + } + catch (WebSocketException socketException) when (!WebSocketCanSend(ws)) + { + // this can happen when we send the CloseFrame to the client and try to write afterwards + _logger.SendFailed(_connectionId, socketException); + break; } catch (Exception ex) { @@ -189,5 +198,12 @@ namespace Microsoft.AspNetCore.Sockets.Internal.Transports } } } + + private static bool WebSocketCanSend(WebSocket ws) + { + return !(ws.State == WebSocketState.Aborted || + ws.State == WebSocketState.Closed || + ws.State == WebSocketState.CloseSent); + } } } diff --git a/test/Microsoft.AspNetCore.Sockets.Tests/WebSocketsTests.cs b/test/Microsoft.AspNetCore.Sockets.Tests/WebSocketsTests.cs index 15d179fb11..40ca21e0a9 100644 --- a/test/Microsoft.AspNetCore.Sockets.Tests/WebSocketsTests.cs +++ b/test/Microsoft.AspNetCore.Sockets.Tests/WebSocketsTests.cs @@ -108,7 +108,7 @@ namespace Microsoft.AspNetCore.Sockets.Tests { var options = new WebSocketOptions() { - CloseTimeout = TimeSpan.FromSeconds(1) + CloseTimeout = TimeSpan.FromMilliseconds(100) }; var ws = new WebSocketsTransport(options, transportSide, connectionId: string.Empty, loggerFactory: new LoggerFactory());