diff --git a/src/Discord.Net.Core/Net/WebSockets/IWebSocketClient.cs b/src/Discord.Net.Core/Net/WebSockets/IWebSocketClient.cs index 14b41cce1..6791af354 100644 --- a/src/Discord.Net.Core/Net/WebSockets/IWebSocketClient.cs +++ b/src/Discord.Net.Core/Net/WebSockets/IWebSocketClient.cs @@ -14,7 +14,7 @@ namespace Discord.Net.WebSockets void SetCancelToken(CancellationToken cancelToken); Task ConnectAsync(string host); - Task DisconnectAsync(); + Task DisconnectAsync(int closeCode = 1000); Task SendAsync(byte[] data, int index, int count, bool isText); } diff --git a/src/Discord.Net.Providers.WS4Net/WS4NetClient.cs b/src/Discord.Net.Providers.WS4Net/WS4NetClient.cs index ef99c8045..50f19b778 100644 --- a/src/Discord.Net.Providers.WS4Net/WS4NetClient.cs +++ b/src/Discord.Net.Providers.WS4Net/WS4NetClient.cs @@ -40,7 +40,7 @@ namespace Discord.Net.Providers.WS4Net { if (disposing) { - DisconnectInternalAsync(true).GetAwaiter().GetResult(); + DisconnectInternalAsync(isDisposing: true).GetAwaiter().GetResult(); _lock?.Dispose(); _cancelTokenSource?.Dispose(); } @@ -92,19 +92,19 @@ namespace Discord.Net.Providers.WS4Net _waitUntilConnect.Wait(_cancelToken); } - public async Task DisconnectAsync() + public async Task DisconnectAsync(int closeCode = 1000) { await _lock.WaitAsync().ConfigureAwait(false); try { - await DisconnectInternalAsync().ConfigureAwait(false); + await DisconnectInternalAsync(closeCode: closeCode).ConfigureAwait(false); } finally { _lock.Release(); } } - private Task DisconnectInternalAsync(bool isDisposing = false) + private Task DisconnectInternalAsync(int closeCode = 1000, bool isDisposing = false) { _disconnectCancelTokenSource.Cancel(); if (_client == null) @@ -112,7 +112,7 @@ namespace Discord.Net.Providers.WS4Net if (_client.State == WebSocketState.Open) { - try { _client.Close(1000, ""); } + try { _client.Close(closeCode, ""); } catch { } } diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs index ff6d17240..3a46dcf21 100644 --- a/src/Discord.Net.Rest/DiscordRestApiClient.cs +++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs @@ -47,7 +47,7 @@ namespace Discord.API internal ulong? CurrentUserId { get; set; } public RateLimitPrecision RateLimitPrecision { get; private set; } internal bool UseSystemClock { get; set; } - + internal JsonSerializer Serializer => _serializer; /// Unknown OAuth token type. @@ -164,7 +164,7 @@ namespace Discord.API try { _loginCancelToken?.Cancel(false); } catch { } - await DisconnectInternalAsync().ConfigureAwait(false); + await DisconnectInternalAsync(null).ConfigureAwait(false); await RequestQueue.ClearAsync().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 DisconnectInternalAsync() => Task.Delay(0); + internal virtual Task DisconnectInternalAsync(Exception ex = null) => Task.Delay(0); //Core internal Task SendAsync(string method, Expression> endpointExpr, BucketIds ids, @@ -1062,7 +1062,7 @@ namespace Discord.API { foreach (var roleId in args.RoleIds.Value) Preconditions.NotEqual(roleId, 0, nameof(roleId)); - } + } options = RequestOptions.CreateOrClone(options); diff --git a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs index 9313f0711..ef97615e2 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs @@ -164,26 +164,17 @@ namespace Discord.API } } - public async Task DisconnectAsync() + public async Task DisconnectAsync(Exception ex = null) { await _stateLock.WaitAsync().ConfigureAwait(false); try { - await DisconnectInternalAsync().ConfigureAwait(false); - } - finally { _stateLock.Release(); } - } - public async Task DisconnectAsync(Exception ex) - { - await _stateLock.WaitAsync().ConfigureAwait(false); - try - { - await DisconnectInternalAsync().ConfigureAwait(false); + await DisconnectInternalAsync(ex).ConfigureAwait(false); } finally { _stateLock.Release(); } } /// This client is not configured with WebSocket support. - internal override async Task DisconnectInternalAsync() + internal override async Task DisconnectInternalAsync(Exception ex = null) { if (WebSocketClient == null) throw new NotSupportedException("This client is not configured with WebSocket support."); @@ -194,6 +185,9 @@ namespace Discord.API try { _connectCancelToken?.Cancel(false); } catch { } + if (ex is GatewayReconnectException) + await WebSocketClient.DisconnectAsync(4000); + else await WebSocketClient.DisconnectAsync().ConfigureAwait(false); ConnectionState = ConnectionState.Disconnected; diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index ed142d001..1819966b3 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -264,7 +264,7 @@ namespace Discord.WebSocket { await _gatewayLogger.DebugAsync("Disconnecting ApiClient").ConfigureAwait(false); - await ApiClient.DisconnectAsync().ConfigureAwait(false); + await ApiClient.DisconnectAsync(ex).ConfigureAwait(false); //Wait for tasks to complete await _gatewayLogger.DebugAsync("Waiting for heartbeater").ConfigureAwait(false); @@ -511,7 +511,7 @@ namespace Discord.WebSocket case GatewayOpCode.Reconnect: { await _gatewayLogger.DebugAsync("Received Reconnect").ConfigureAwait(false); - _connection.Error(new Exception("Server requested a reconnect")); + _connection.Error(new GatewayReconnectException("Server requested a reconnect")); } break; case GatewayOpCode.Dispatch: @@ -1689,7 +1689,7 @@ namespace Discord.WebSocket { if (ConnectionState == ConnectionState.Connected && (_guildDownloadTask?.IsCompleted ?? true)) { - _connection.Error(new Exception("Server missed last heartbeat")); + _connection.Error(new GatewayReconnectException("Server missed last heartbeat")); return; } } diff --git a/src/Discord.Net.WebSocket/GatewayReconnectException.cs b/src/Discord.Net.WebSocket/GatewayReconnectException.cs new file mode 100644 index 000000000..1a8024558 --- /dev/null +++ b/src/Discord.Net.WebSocket/GatewayReconnectException.cs @@ -0,0 +1,22 @@ +using System; + +namespace Discord.WebSocket +{ + /// + /// An exception thrown when the gateway client has been requested to + /// reconnect. + /// + public class GatewayReconnectException : Exception + { + /// + /// Creates a new instance of the + /// type. + /// + /// + /// The reason why the gateway has been requested to reconnect. + /// + public GatewayReconnectException(string message) + : base(message) + { } + } +} diff --git a/src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs b/src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs index 36a6fea4f..4723ae57a 100644 --- a/src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs +++ b/src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs @@ -44,7 +44,7 @@ namespace Discord.Net.WebSockets { if (disposing) { - DisconnectInternalAsync(true).GetAwaiter().GetResult(); + DisconnectInternalAsync(isDisposing: true).GetAwaiter().GetResult(); _disconnectTokenSource?.Dispose(); _cancelTokenSource?.Dispose(); _lock?.Dispose(); @@ -94,19 +94,19 @@ namespace Discord.Net.WebSockets _task = RunAsync(_cancelToken); } - public async Task DisconnectAsync() + public async Task DisconnectAsync(int closeCode = 1000) { await _lock.WaitAsync().ConfigureAwait(false); try { - await DisconnectInternalAsync().ConfigureAwait(false); + await DisconnectInternalAsync(closeCode: closeCode).ConfigureAwait(false); } finally { _lock.Release(); } } - private async Task DisconnectInternalAsync(bool isDisposing = false) + private async Task DisconnectInternalAsync(int closeCode = 1000, bool isDisposing = false) { try { _disconnectTokenSource.Cancel(false); } catch { } @@ -117,7 +117,8 @@ namespace Discord.Net.WebSockets { if (!isDisposing) { - try { await _client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", new CancellationToken()); } + var status = (WebSocketCloseStatus)closeCode; + try { await _client.CloseOutputAsync(status, "", new CancellationToken()); } catch { } } try { _client.Dispose(); } @@ -141,7 +142,7 @@ namespace Discord.Net.WebSockets await _lock.WaitAsync().ConfigureAwait(false); try { - await DisconnectInternalAsync(false); + await DisconnectInternalAsync(isDisposing: false); } finally {