[apibrk] change: Specify WebSocket close code (#1500)

* API breaking change: Specify WebSocket close code

Should fix #1479 and help overall with resuming sessions.

* Also try to resume on missed heartbeats
This commit is contained in:
Monica S
2020-04-25 12:12:57 +01:00
committed by GitHub
parent 6d8e216545
commit ed869bd78b
7 changed files with 48 additions and 31 deletions

View File

@@ -14,7 +14,7 @@ namespace Discord.Net.WebSockets
void SetCancelToken(CancellationToken cancelToken); void SetCancelToken(CancellationToken cancelToken);
Task ConnectAsync(string host); Task ConnectAsync(string host);
Task DisconnectAsync(); Task DisconnectAsync(int closeCode = 1000);
Task SendAsync(byte[] data, int index, int count, bool isText); Task SendAsync(byte[] data, int index, int count, bool isText);
} }

View File

@@ -40,7 +40,7 @@ namespace Discord.Net.Providers.WS4Net
{ {
if (disposing) if (disposing)
{ {
DisconnectInternalAsync(true).GetAwaiter().GetResult(); DisconnectInternalAsync(isDisposing: true).GetAwaiter().GetResult();
_lock?.Dispose(); _lock?.Dispose();
_cancelTokenSource?.Dispose(); _cancelTokenSource?.Dispose();
} }
@@ -92,19 +92,19 @@ namespace Discord.Net.Providers.WS4Net
_waitUntilConnect.Wait(_cancelToken); _waitUntilConnect.Wait(_cancelToken);
} }
public async Task DisconnectAsync() public async Task DisconnectAsync(int closeCode = 1000)
{ {
await _lock.WaitAsync().ConfigureAwait(false); await _lock.WaitAsync().ConfigureAwait(false);
try try
{ {
await DisconnectInternalAsync().ConfigureAwait(false); await DisconnectInternalAsync(closeCode: closeCode).ConfigureAwait(false);
} }
finally finally
{ {
_lock.Release(); _lock.Release();
} }
} }
private Task DisconnectInternalAsync(bool isDisposing = false) private Task DisconnectInternalAsync(int closeCode = 1000, bool isDisposing = false)
{ {
_disconnectCancelTokenSource.Cancel(); _disconnectCancelTokenSource.Cancel();
if (_client == null) if (_client == null)
@@ -112,7 +112,7 @@ namespace Discord.Net.Providers.WS4Net
if (_client.State == WebSocketState.Open) if (_client.State == WebSocketState.Open)
{ {
try { _client.Close(1000, ""); } try { _client.Close(closeCode, ""); }
catch { } catch { }
} }

View File

@@ -164,7 +164,7 @@ namespace Discord.API
try { _loginCancelToken?.Cancel(false); } try { _loginCancelToken?.Cancel(false); }
catch { } catch { }
await DisconnectInternalAsync().ConfigureAwait(false); await DisconnectInternalAsync(null).ConfigureAwait(false);
await RequestQueue.ClearAsync().ConfigureAwait(false); await RequestQueue.ClearAsync().ConfigureAwait(false);
await RequestQueue.SetCancelTokenAsync(CancellationToken.None).ConfigureAwait(false); await RequestQueue.SetCancelTokenAsync(CancellationToken.None).ConfigureAwait(false);
@@ -175,7 +175,7 @@ namespace Discord.API
} }
internal virtual Task ConnectInternalAsync() => Task.Delay(0); internal virtual Task ConnectInternalAsync() => Task.Delay(0);
internal virtual Task DisconnectInternalAsync() => Task.Delay(0); internal virtual Task DisconnectInternalAsync(Exception ex = null) => Task.Delay(0);
//Core //Core
internal Task SendAsync(string method, Expression<Func<string>> endpointExpr, BucketIds ids, internal Task SendAsync(string method, Expression<Func<string>> endpointExpr, BucketIds ids,

View File

@@ -164,26 +164,17 @@ namespace Discord.API
} }
} }
public async Task DisconnectAsync() public async Task DisconnectAsync(Exception ex = null)
{ {
await _stateLock.WaitAsync().ConfigureAwait(false); await _stateLock.WaitAsync().ConfigureAwait(false);
try try
{ {
await DisconnectInternalAsync().ConfigureAwait(false); await DisconnectInternalAsync(ex).ConfigureAwait(false);
}
finally { _stateLock.Release(); }
}
public async Task DisconnectAsync(Exception ex)
{
await _stateLock.WaitAsync().ConfigureAwait(false);
try
{
await DisconnectInternalAsync().ConfigureAwait(false);
} }
finally { _stateLock.Release(); } finally { _stateLock.Release(); }
} }
/// <exception cref="NotSupportedException">This client is not configured with WebSocket support.</exception> /// <exception cref="NotSupportedException">This client is not configured with WebSocket support.</exception>
internal override async Task DisconnectInternalAsync() internal override async Task DisconnectInternalAsync(Exception ex = null)
{ {
if (WebSocketClient == null) if (WebSocketClient == null)
throw new NotSupportedException("This client is not configured with WebSocket support."); throw new NotSupportedException("This client is not configured with WebSocket support.");
@@ -194,6 +185,9 @@ namespace Discord.API
try { _connectCancelToken?.Cancel(false); } try { _connectCancelToken?.Cancel(false); }
catch { } catch { }
if (ex is GatewayReconnectException)
await WebSocketClient.DisconnectAsync(4000);
else
await WebSocketClient.DisconnectAsync().ConfigureAwait(false); await WebSocketClient.DisconnectAsync().ConfigureAwait(false);
ConnectionState = ConnectionState.Disconnected; ConnectionState = ConnectionState.Disconnected;

View File

@@ -264,7 +264,7 @@ namespace Discord.WebSocket
{ {
await _gatewayLogger.DebugAsync("Disconnecting ApiClient").ConfigureAwait(false); await _gatewayLogger.DebugAsync("Disconnecting ApiClient").ConfigureAwait(false);
await ApiClient.DisconnectAsync().ConfigureAwait(false); await ApiClient.DisconnectAsync(ex).ConfigureAwait(false);
//Wait for tasks to complete //Wait for tasks to complete
await _gatewayLogger.DebugAsync("Waiting for heartbeater").ConfigureAwait(false); await _gatewayLogger.DebugAsync("Waiting for heartbeater").ConfigureAwait(false);
@@ -511,7 +511,7 @@ namespace Discord.WebSocket
case GatewayOpCode.Reconnect: case GatewayOpCode.Reconnect:
{ {
await _gatewayLogger.DebugAsync("Received Reconnect").ConfigureAwait(false); await _gatewayLogger.DebugAsync("Received Reconnect").ConfigureAwait(false);
_connection.Error(new Exception("Server requested a reconnect")); _connection.Error(new GatewayReconnectException("Server requested a reconnect"));
} }
break; break;
case GatewayOpCode.Dispatch: case GatewayOpCode.Dispatch:
@@ -1689,7 +1689,7 @@ namespace Discord.WebSocket
{ {
if (ConnectionState == ConnectionState.Connected && (_guildDownloadTask?.IsCompleted ?? true)) if (ConnectionState == ConnectionState.Connected && (_guildDownloadTask?.IsCompleted ?? true))
{ {
_connection.Error(new Exception("Server missed last heartbeat")); _connection.Error(new GatewayReconnectException("Server missed last heartbeat"));
return; return;
} }
} }

View File

@@ -0,0 +1,22 @@
using System;
namespace Discord.WebSocket
{
/// <summary>
/// An exception thrown when the gateway client has been requested to
/// reconnect.
/// </summary>
public class GatewayReconnectException : Exception
{
/// <summary>
/// Creates a new instance of the
/// <see cref="GatewayReconnectException"/> type.
/// </summary>
/// <param name="message">
/// The reason why the gateway has been requested to reconnect.
/// </param>
public GatewayReconnectException(string message)
: base(message)
{ }
}
}

View File

@@ -44,7 +44,7 @@ namespace Discord.Net.WebSockets
{ {
if (disposing) if (disposing)
{ {
DisconnectInternalAsync(true).GetAwaiter().GetResult(); DisconnectInternalAsync(isDisposing: true).GetAwaiter().GetResult();
_disconnectTokenSource?.Dispose(); _disconnectTokenSource?.Dispose();
_cancelTokenSource?.Dispose(); _cancelTokenSource?.Dispose();
_lock?.Dispose(); _lock?.Dispose();
@@ -94,19 +94,19 @@ namespace Discord.Net.WebSockets
_task = RunAsync(_cancelToken); _task = RunAsync(_cancelToken);
} }
public async Task DisconnectAsync() public async Task DisconnectAsync(int closeCode = 1000)
{ {
await _lock.WaitAsync().ConfigureAwait(false); await _lock.WaitAsync().ConfigureAwait(false);
try try
{ {
await DisconnectInternalAsync().ConfigureAwait(false); await DisconnectInternalAsync(closeCode: closeCode).ConfigureAwait(false);
} }
finally finally
{ {
_lock.Release(); _lock.Release();
} }
} }
private async Task DisconnectInternalAsync(bool isDisposing = false) private async Task DisconnectInternalAsync(int closeCode = 1000, bool isDisposing = false)
{ {
try { _disconnectTokenSource.Cancel(false); } try { _disconnectTokenSource.Cancel(false); }
catch { } catch { }
@@ -117,7 +117,8 @@ namespace Discord.Net.WebSockets
{ {
if (!isDisposing) if (!isDisposing)
{ {
try { await _client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", new CancellationToken()); } var status = (WebSocketCloseStatus)closeCode;
try { await _client.CloseOutputAsync(status, "", new CancellationToken()); }
catch { } catch { }
} }
try { _client.Dispose(); } try { _client.Dispose(); }
@@ -141,7 +142,7 @@ namespace Discord.Net.WebSockets
await _lock.WaitAsync().ConfigureAwait(false); await _lock.WaitAsync().ConfigureAwait(false);
try try
{ {
await DisconnectInternalAsync(false); await DisconnectInternalAsync(isDisposing: false);
} }
finally finally
{ {