Give Client a chance to receive Close Frame from Server (#730)
This commit is contained in:
parent
e349329dc7
commit
a4053acd06
|
|
@ -62,6 +62,9 @@ namespace Microsoft.AspNetCore.Sockets.Client.Internal
|
||||||
private static readonly Action<ILogger, DateTime, string, Exception> _closingWebSocketFailed =
|
private static readonly Action<ILogger, DateTime, string, Exception> _closingWebSocketFailed =
|
||||||
LoggerMessage.Define<DateTime, string>(LogLevel.Information, 16, "{time}: Connection Id {connectionId}: Closing webSocket failed.");
|
LoggerMessage.Define<DateTime, string>(LogLevel.Information, 16, "{time}: Connection Id {connectionId}: Closing webSocket failed.");
|
||||||
|
|
||||||
|
private static readonly Action<ILogger, DateTime, string, Exception> _cancelMessage =
|
||||||
|
LoggerMessage.Define<DateTime, string>(LogLevel.Debug, 17, "{time}: Connection Id {connectionId}: Canceled passing message to application.");
|
||||||
|
|
||||||
// Category: ServerSentEventsTransport and LongPollingTransport
|
// Category: ServerSentEventsTransport and LongPollingTransport
|
||||||
private static readonly Action<ILogger, DateTime, string, int, Uri, Exception> _sendingMessages =
|
private static readonly Action<ILogger, DateTime, string, int, Uri, Exception> _sendingMessages =
|
||||||
LoggerMessage.Define<DateTime, string, int, Uri>(LogLevel.Debug, 9, "{time}: Connection Id {connectionId}: Sending {count} message(s) to the server using url: {url}.");
|
LoggerMessage.Define<DateTime, string, int, Uri>(LogLevel.Debug, 9, "{time}: Connection Id {connectionId}: Sending {count} message(s) to the server using url: {url}.");
|
||||||
|
|
@ -283,6 +286,14 @@ namespace Microsoft.AspNetCore.Sockets.Client.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void CancelMessage(this ILogger logger, string connectionId)
|
||||||
|
{
|
||||||
|
if (logger.IsEnabled(LogLevel.Debug))
|
||||||
|
{
|
||||||
|
_cancelMessage(logger, DateTime.Now, connectionId, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void SendingMessages(this ILogger logger, string connectionId, int count, Uri url)
|
public static void SendingMessages(this ILogger logger, string connectionId, int count, Uri url)
|
||||||
{
|
{
|
||||||
if (logger.IsEnabled(LogLevel.Debug))
|
if (logger.IsEnabled(LogLevel.Debug))
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
private readonly ClientWebSocket _webSocket = new ClientWebSocket();
|
private readonly ClientWebSocket _webSocket = new ClientWebSocket();
|
||||||
private Channel<byte[], SendMessage> _application;
|
private Channel<byte[], SendMessage> _application;
|
||||||
private readonly CancellationTokenSource _transportCts = new CancellationTokenSource();
|
private readonly CancellationTokenSource _transportCts = new CancellationTokenSource();
|
||||||
|
private readonly CancellationTokenSource _receiveCts = new CancellationTokenSource();
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private string _connectionId;
|
private string _connectionId;
|
||||||
|
|
||||||
|
|
@ -80,7 +81,7 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (!_transportCts.Token.IsCancellationRequested)
|
while (!_receiveCts.Token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
const int bufferSize = 4096;
|
const int bufferSize = 4096;
|
||||||
var totalBytes = 0;
|
var totalBytes = 0;
|
||||||
|
|
@ -91,7 +92,7 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
var buffer = new ArraySegment<byte>(new byte[bufferSize]);
|
var buffer = new ArraySegment<byte>(new byte[bufferSize]);
|
||||||
|
|
||||||
//Exceptions are handled above where the send and receive tasks are being run.
|
//Exceptions are handled above where the send and receive tasks are being run.
|
||||||
receiveResult = await _webSocket.ReceiveAsync(buffer, _transportCts.Token);
|
receiveResult = await _webSocket.ReceiveAsync(buffer, _receiveCts.Token);
|
||||||
if (receiveResult.MessageType == WebSocketMessageType.Close)
|
if (receiveResult.MessageType == WebSocketMessageType.Close)
|
||||||
{
|
{
|
||||||
_logger.WebSocketClosed(_connectionId, receiveResult.CloseStatus);
|
_logger.WebSocketClosed(_connectionId, receiveResult.CloseStatus);
|
||||||
|
|
@ -129,15 +130,25 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
Buffer.BlockCopy(incomingMessage[0].Array, incomingMessage[0].Offset, messageBuffer, 0, incomingMessage[0].Count);
|
Buffer.BlockCopy(incomingMessage[0].Array, incomingMessage[0].Offset, messageBuffer, 0, incomingMessage[0].Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.MessageToApp(_connectionId, messageBuffer.Length);
|
try
|
||||||
while (await _application.Out.WaitToWriteAsync(_transportCts.Token))
|
|
||||||
{
|
{
|
||||||
if (_application.Out.TryWrite(messageBuffer))
|
if (!_transportCts.Token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
incomingMessage.Clear();
|
_logger.MessageToApp(_connectionId, messageBuffer.Length);
|
||||||
break;
|
while (await _application.Out.WaitToWriteAsync(_transportCts.Token))
|
||||||
|
{
|
||||||
|
if (_application.Out.TryWrite(messageBuffer))
|
||||||
|
{
|
||||||
|
incomingMessage.Clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
_logger.CancelMessage(_connectionId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
|
|
@ -198,7 +209,7 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_logger.SendStopped(_connectionId);
|
_logger.SendStopped(_connectionId);
|
||||||
_transportCts.Cancel();
|
TriggerCancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -248,7 +259,7 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
await _webSocket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None);
|
await _webSocket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None);
|
||||||
|
|
||||||
// shutdown the transport after a timeout in case the server does not send close frame
|
// shutdown the transport after a timeout in case the server does not send close frame
|
||||||
_transportCts.CancelAfter(TimeSpan.FromSeconds(5));
|
TriggerCancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -258,5 +269,12 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
_logger.ClosingWebSocketFailed(_connectionId, ex);
|
_logger.ClosingWebSocketFailed(_connectionId, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TriggerCancel()
|
||||||
|
{
|
||||||
|
// Give server 5 seconds to respond with a close frame for graceful close.
|
||||||
|
_receiveCts.CancelAfter(TimeSpan.FromSeconds(5));
|
||||||
|
_transportCts.Cancel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
await webSocketsTransport.StartAsync(new Uri(_serverFixture.WebSocketsUrl + "/echo"), channelConnection,
|
await webSocketsTransport.StartAsync(new Uri(_serverFixture.WebSocketsUrl + "/echo"), channelConnection,
|
||||||
TransferMode.Binary, connectionId: string.Empty);
|
TransferMode.Binary, connectionId: string.Empty);
|
||||||
connectionToTransport.Out.TryComplete();
|
connectionToTransport.Out.TryComplete();
|
||||||
await webSocketsTransport.Running.OrTimeout();
|
await webSocketsTransport.Running.OrTimeout(TimeSpan.FromSeconds(10));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue