[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:
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ namespace Discord.API
|
|||||||
internal ulong? CurrentUserId { get; set; }
|
internal ulong? CurrentUserId { get; set; }
|
||||||
public RateLimitPrecision RateLimitPrecision { get; private set; }
|
public RateLimitPrecision RateLimitPrecision { get; private set; }
|
||||||
internal bool UseSystemClock { get; set; }
|
internal bool UseSystemClock { get; set; }
|
||||||
|
|
||||||
internal JsonSerializer Serializer => _serializer;
|
internal JsonSerializer Serializer => _serializer;
|
||||||
|
|
||||||
/// <exception cref="ArgumentException">Unknown OAuth token type.</exception>
|
/// <exception cref="ArgumentException">Unknown OAuth token type.</exception>
|
||||||
@@ -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,
|
||||||
@@ -1062,7 +1062,7 @@ namespace Discord.API
|
|||||||
{
|
{
|
||||||
foreach (var roleId in args.RoleIds.Value)
|
foreach (var roleId in args.RoleIds.Value)
|
||||||
Preconditions.NotEqual(roleId, 0, nameof(roleId));
|
Preconditions.NotEqual(roleId, 0, nameof(roleId));
|
||||||
}
|
}
|
||||||
|
|
||||||
options = RequestOptions.CreateOrClone(options);
|
options = RequestOptions.CreateOrClone(options);
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
22
src/Discord.Net.WebSocket/GatewayReconnectException.cs
Normal file
22
src/Discord.Net.WebSocket/GatewayReconnectException.cs
Normal 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)
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user