| @@ -21,15 +21,15 @@ namespace Discord.API | |||
| => _http.Get<APIResponses.Gateway>(Endpoints.Gateway); | |||
| public async Task<APIResponses.AuthRegister> LoginAnonymous(string username) | |||
| { | |||
| var fingerprintResponse = await _http.Post<APIResponses.AuthFingerprint>(Endpoints.AuthFingerprint); | |||
| var fingerprintResponse = await _http.Post<APIResponses.AuthFingerprint>(Endpoints.AuthFingerprint).ConfigureAwait(false); | |||
| var registerRequest = new APIRequests.AuthRegisterRequest { Fingerprint = fingerprintResponse.Fingerprint, Username = username }; | |||
| var registerResponse = await _http.Post<APIResponses.AuthRegister>(Endpoints.AuthRegister, registerRequest); | |||
| var registerResponse = await _http.Post<APIResponses.AuthRegister>(Endpoints.AuthRegister, registerRequest).ConfigureAwait(false); | |||
| return registerResponse; | |||
| } | |||
| public async Task<APIResponses.AuthLogin> Login(string email, string password) | |||
| { | |||
| var request = new APIRequests.AuthLogin { Email = email, Password = password }; | |||
| var response = await _http.Post<APIResponses.AuthLogin>(Endpoints.AuthLogin, request); | |||
| var response = await _http.Post<APIResponses.AuthLogin>(Endpoints.AuthLogin, request).ConfigureAwait(false); | |||
| return response; | |||
| } | |||
| public Task Logout() | |||
| @@ -24,7 +24,7 @@ namespace Discord | |||
| if (name == null) throw new ArgumentNullException(nameof(name)); | |||
| if (region == null) throw new ArgumentNullException(nameof(region)); | |||
| var response = await _api.CreateServer(name, region); | |||
| var response = await _api.CreateServer(name, region).ConfigureAwait(false); | |||
| return _servers.Update(response.Id, response); | |||
| } | |||
| @@ -37,7 +37,7 @@ namespace Discord | |||
| CheckReady(); | |||
| if (serverId == null) throw new ArgumentNullException(nameof(serverId)); | |||
| try { await _api.LeaveServer(serverId); } | |||
| try { await _api.LeaveServer(serverId).ConfigureAwait(false); } | |||
| catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } | |||
| return _servers.Remove(serverId); | |||
| } | |||
| @@ -54,7 +54,7 @@ namespace Discord | |||
| if (name == null) throw new ArgumentNullException(nameof(name)); | |||
| if (type == null) throw new ArgumentNullException(nameof(type)); | |||
| var response = await _api.CreateChannel(serverId, name, type); | |||
| var response = await _api.CreateChannel(serverId, name, type).ConfigureAwait(false); | |||
| return _channels.Update(response.Id, serverId, response); | |||
| } | |||
| @@ -67,7 +67,7 @@ namespace Discord | |||
| CheckReady(); | |||
| if (channelId == null) throw new ArgumentNullException(nameof(channelId)); | |||
| try { await _api.DestroyChannel(channelId); } | |||
| try { await _api.DestroyChannel(channelId).ConfigureAwait(false); } | |||
| catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } | |||
| return _channels.Remove(channelId); | |||
| } | |||
| @@ -108,7 +108,7 @@ namespace Discord | |||
| if (serverId == null) throw new ArgumentNullException(nameof(serverId)); | |||
| if (userId == null) throw new ArgumentNullException(nameof(userId)); | |||
| try { await _api.Unban(serverId, userId); } | |||
| try { await _api.Unban(serverId, userId).ConfigureAwait(false); } | |||
| catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } | |||
| } | |||
| @@ -139,7 +139,7 @@ namespace Discord | |||
| if (maxAge <= 0) throw new ArgumentOutOfRangeException(nameof(maxAge)); | |||
| if (maxUses <= 0) throw new ArgumentOutOfRangeException(nameof(maxUses)); | |||
| var response = await _api.CreateInvite(channelId, maxAge, maxUses, isTemporary, hasXkcdPass); | |||
| var response = await _api.CreateInvite(channelId, maxAge, maxUses, isTemporary, hasXkcdPass).ConfigureAwait(false); | |||
| _channels.Update(response.Channel.Id, response.Server.Id, response.Channel); | |||
| _servers.Update(response.Server.Id, response.Server); | |||
| _users.Update(response.Inviter.Id, response.Inviter); | |||
| @@ -163,7 +163,7 @@ namespace Discord | |||
| CheckReady(); | |||
| if (id == null) throw new ArgumentNullException(nameof(id)); | |||
| var response = await _api.GetInvite(id); | |||
| var response = await _api.GetInvite(id).ConfigureAwait(false); | |||
| return new Invite(response.Code, response.XkcdPass, this) | |||
| { | |||
| ChannelId = response.Channel.Id, | |||
| @@ -195,8 +195,8 @@ namespace Discord | |||
| id = id.Substring(0, id.Length - 1); | |||
| //Check if this is a human-readable link and get its ID | |||
| var response = await _api.GetInvite(id); | |||
| await _api.AcceptInvite(response.Code); | |||
| var response = await _api.GetInvite(id).ConfigureAwait(false); | |||
| await _api.AcceptInvite(response.Code).ConfigureAwait(false); | |||
| } | |||
| /// <summary> Deletes the provided invite. </summary> | |||
| @@ -208,8 +208,8 @@ namespace Discord | |||
| try | |||
| { | |||
| //Check if this is a human-readable link and get its ID | |||
| var response = await _api.GetInvite(id); | |||
| await _api.DeleteInvite(response.Code); | |||
| var response = await _api.GetInvite(id).ConfigureAwait(false); | |||
| await _api.DeleteInvite(response.Code).ConfigureAwait(false); | |||
| } | |||
| catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } | |||
| } | |||
| @@ -257,31 +257,28 @@ namespace Discord | |||
| } | |||
| else | |||
| { | |||
| var msg = await _api.SendMessage(channelId, blockText, mentions, nonce); | |||
| var msg = await _api.SendMessage(channelId, blockText, mentions, nonce).ConfigureAwait(false); | |||
| result[i] = _messages.Update(msg.Id, channelId, msg); | |||
| result[i].Nonce = nonce; | |||
| try { RaiseMessageSent(result[i]); } catch { } | |||
| } | |||
| await Task.Delay(1000); | |||
| await Task.Delay(1000).ConfigureAwait(false); | |||
| } | |||
| return result; | |||
| } | |||
| /// <summary> Sends a private message to the provided channel. </summary> | |||
| public async Task<Message[]> SendPrivateMessage(User user, string text) | |||
| => await SendMessage(await GetPMChannel(user), text, new string[0]); | |||
| { | |||
| var channel = await GetPMChannel(user).ConfigureAwait(false); | |||
| return await SendMessage(channel, text, new string[0]).ConfigureAwait(false); | |||
| } | |||
| /// <summary> Sends a private message to the provided channel. </summary> | |||
| public async Task<Message[]> SendPrivateMessage(string userId, string text) | |||
| => await SendMessage(await GetPMChannel(userId), text, new string[0]); | |||
| /*/// <summary> Sends a private message to the provided user, mentioning certain users. </summary> | |||
| /// <remarks> While not required, it is recommended to include a mention reference in the text (see User.Mention). </remarks> | |||
| public async Task<Message[]> SendPrivateMessage(User user, string text, string[] mentions) | |||
| => SendMessage(await GetOrCreatePMChannel(user), text, mentions); | |||
| /// <summary> Sends a private message to the provided user, mentioning certain users. </summary> | |||
| /// <remarks> While not required, it is recommended to include a mention reference in the text (see User.Mention). </remarks> | |||
| public async Task<Message[]> SendPrivateMessage(string userId, string text, string[] mentions) | |||
| => SendMessage(await GetOrCreatePMChannel(userId), text, mentions);*/ | |||
| { | |||
| var channel = await GetPMChannel(userId).ConfigureAwait(false); | |||
| return await SendMessage(channel, text, new string[0]).ConfigureAwait(false); | |||
| } | |||
| /// <summary> Edits a message the provided message. </summary> | |||
| public Task EditMessage(Message message, string text) | |||
| @@ -313,7 +310,7 @@ namespace Discord | |||
| if (text.Length > DiscordAPI.MaxMessageSize) | |||
| text = text.Substring(0, DiscordAPI.MaxMessageSize); | |||
| var msg = await _api.EditMessage(channelId, messageId, text, mentions); | |||
| var msg = await _api.EditMessage(channelId, messageId, text, mentions).ConfigureAwait(false); | |||
| _messages.Update(msg.Id, channelId, msg); | |||
| } | |||
| @@ -329,7 +326,7 @@ namespace Discord | |||
| try | |||
| { | |||
| await _api.DeleteMessage(channelId, msgId); | |||
| await _api.DeleteMessage(channelId, msgId).ConfigureAwait(false); | |||
| _messages.Remove(msgId); | |||
| } | |||
| catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } | |||
| @@ -343,7 +340,7 @@ namespace Discord | |||
| { | |||
| try | |||
| { | |||
| await _api.DeleteMessage(msg.ChannelId, msg.Id); | |||
| await _api.DeleteMessage(msg.ChannelId, msg.Id).ConfigureAwait(false); | |||
| } | |||
| catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } | |||
| } | |||
| @@ -357,7 +354,7 @@ namespace Discord | |||
| { | |||
| try | |||
| { | |||
| await _api.DeleteMessage(channelId, msgId); | |||
| await _api.DeleteMessage(channelId, msgId).ConfigureAwait(false); | |||
| } | |||
| catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } | |||
| } | |||
| @@ -404,7 +401,7 @@ namespace Discord | |||
| { | |||
| try | |||
| { | |||
| var msgs = await _api.GetMessages(channel.Id, count); | |||
| var msgs = await _api.GetMessages(channel.Id, count).ConfigureAwait(false); | |||
| return msgs.OrderBy(x => x.Timestamp) | |||
| .Select(x => | |||
| { | |||
| @@ -503,21 +500,21 @@ namespace Discord | |||
| public async Task ChangeUsername(string newName, string currentEmail, string currentPassword) | |||
| { | |||
| CheckReady(); | |||
| var response = await _api.ChangeUsername(newName, currentEmail, currentPassword); | |||
| var response = await _api.ChangeUsername(newName, currentEmail, currentPassword).ConfigureAwait(false); | |||
| _users.Update(response.Id, response); | |||
| } | |||
| /// <summary> Changes your email to newEmail. </summary> | |||
| public async Task ChangeEmail(string newEmail, string currentPassword) | |||
| { | |||
| CheckReady(); | |||
| var response = await _api.ChangeEmail(newEmail, currentPassword); | |||
| var response = await _api.ChangeEmail(newEmail, currentPassword).ConfigureAwait(false); | |||
| _users.Update(response.Id, response); | |||
| } | |||
| /// <summary> Changes your password to newPassword. </summary> | |||
| public async Task ChangePassword(string newPassword, string currentEmail, string currentPassword) | |||
| { | |||
| CheckReady(); | |||
| var response = await _api.ChangePassword(newPassword, currentEmail, currentPassword); | |||
| var response = await _api.ChangePassword(newPassword, currentEmail, currentPassword).ConfigureAwait(false); | |||
| _users.Update(response.Id, response); | |||
| } | |||
| @@ -526,7 +523,7 @@ namespace Discord | |||
| public async Task ChangeAvatar(AvatarImageType imageType, byte[] bytes, string currentEmail, string currentPassword) | |||
| { | |||
| CheckReady(); | |||
| var response = await _api.ChangeAvatar(imageType, bytes, currentEmail, currentPassword); | |||
| var response = await _api.ChangeAvatar(imageType, bytes, currentEmail, currentPassword).ConfigureAwait(false); | |||
| _users.Update(response.Id, response); | |||
| } | |||
| } | |||
| @@ -403,14 +403,14 @@ namespace Discord | |||
| var channel = user.PrivateChannel; | |||
| if (channel != null) | |||
| return channel; | |||
| return await CreatePMChannel(user?.Id); | |||
| return await CreatePMChannel(user?.Id).ConfigureAwait(false); | |||
| } | |||
| private async Task<Channel> CreatePMChannel(string userId) | |||
| { | |||
| CheckReady(); | |||
| if (userId == null) throw new ArgumentNullException(nameof(userId)); | |||
| var response = await _api.CreatePMChannel(_myId, userId); | |||
| var response = await _api.CreatePMChannel(_myId, userId).ConfigureAwait(false); | |||
| return _channels.Update(response.Id, response); | |||
| } | |||
| @@ -107,21 +107,21 @@ namespace Discord | |||
| //Reconnect if we didn't cause the disconnect | |||
| if (e.WasUnexpected) | |||
| { | |||
| await Task.Delay(_config.ReconnectDelay); | |||
| await Task.Delay(_config.ReconnectDelay).ConfigureAwait(false); | |||
| while (!_disconnectToken.IsCancellationRequested) | |||
| { | |||
| try | |||
| { | |||
| await _webSocket.ReconnectAsync(); | |||
| await _webSocket.ReconnectAsync().ConfigureAwait(false); | |||
| if (_http.Token != null) | |||
| await _webSocket.Login(_http.Token); | |||
| await _webSocket.Login(_http.Token).ConfigureAwait(false); | |||
| break; | |||
| } | |||
| catch (Exception ex) | |||
| { | |||
| RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket reconnect failed: {ex.Message}"); | |||
| //Net is down? We can keep trying to reconnect until the user runs Disconnect() | |||
| await Task.Delay(_config.FailedReconnectDelay); | |||
| await Task.Delay(_config.FailedReconnectDelay).ConfigureAwait(false); | |||
| } | |||
| } | |||
| } | |||
| @@ -141,12 +141,12 @@ namespace Discord | |||
| //Reconnect if we didn't cause the disconnect | |||
| if (e.WasUnexpected) | |||
| { | |||
| await Task.Delay(_config.ReconnectDelay); | |||
| await Task.Delay(_config.ReconnectDelay).ConfigureAwait(false); | |||
| while (!_disconnectToken.IsCancellationRequested) | |||
| { | |||
| try | |||
| { | |||
| await _voiceWebSocket.ReconnectAsync(); | |||
| await _voiceWebSocket.ReconnectAsync().ConfigureAwait(false); | |||
| break; | |||
| } | |||
| catch (Exception ex) | |||
| @@ -154,7 +154,7 @@ namespace Discord | |||
| if (_isDebugMode) | |||
| RaiseOnDebugMessage(DebugMessageType.Connection, $"VoiceSocket reconnect failed: {ex.Message}"); | |||
| //Net is down? We can keep trying to reconnect until the user runs Disconnect() | |||
| await Task.Delay(_config.FailedReconnectDelay); | |||
| await Task.Delay(_config.FailedReconnectDelay).ConfigureAwait(false); | |||
| } | |||
| } | |||
| } | |||
| @@ -426,7 +426,7 @@ namespace Discord | |||
| if (_config.EnableVoice) | |||
| { | |||
| _voiceWebSocket.SetSessionData(data.ServerId, _myId, _sessionId, data.Token); | |||
| await _voiceWebSocket.ConnectAsync("wss://" + data.Endpoint.Split(':')[0]); | |||
| await _voiceWebSocket.ConnectAsync("wss://" + data.Endpoint.Split(':')[0]).ConfigureAwait(false); | |||
| } | |||
| #endif | |||
| } | |||
| @@ -469,7 +469,7 @@ namespace Discord | |||
| APIResponses.SendMessage apiMsg = null; | |||
| try | |||
| { | |||
| apiMsg = await _api.SendMessage(msg.ChannelId, msg.RawText, msg.MentionIds, msg.Nonce); | |||
| apiMsg = await _api.SendMessage(msg.ChannelId, msg.RawText, msg.MentionIds, msg.Nonce).ConfigureAwait(false); | |||
| } | |||
| catch (WebException) { break; } | |||
| catch (HttpException) { hasFailed = true; } | |||
| @@ -483,7 +483,7 @@ namespace Discord | |||
| msg.HasFailed = hasFailed; | |||
| try { RaiseMessageSent(msg); } catch { } | |||
| } | |||
| await Task.Delay(_config.MessageQueueInterval); | |||
| await Task.Delay(_config.MessageQueueInterval).ConfigureAwait(false); | |||
| } | |||
| } | |||
| catch { } | |||
| @@ -499,49 +499,36 @@ namespace Discord | |||
| /// <summary> Connects to the Discord server with the provided token. </summary> | |||
| public async Task Connect(string token) | |||
| { | |||
| await Disconnect(); | |||
| await Disconnect().ConfigureAwait(false); | |||
| if (_isDebugMode) | |||
| RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket is using cached token."); | |||
| await ConnectInternal(token); | |||
| await ConnectInternal(token).ConfigureAwait(false); | |||
| } | |||
| /// <summary> Connects to the Discord server with the provided email and password. </summary> | |||
| /// <returns> Returns a token for future connections. </returns> | |||
| public async Task<string> Connect(string email, string password) | |||
| { | |||
| await Disconnect(); | |||
| await Disconnect().ConfigureAwait(false); | |||
| var response = await _api.Login(email, password); | |||
| var response = await _api.Login(email, password).ConfigureAwait(false); | |||
| if (_isDebugMode) | |||
| RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket got token."); | |||
| return await ConnectInternal(response.Token); | |||
| return await ConnectInternal(response.Token).ConfigureAwait(false); | |||
| } | |||
| /// <summary> Connects to the Discord server as an anonymous user with the provided username. </summary> | |||
| /// <returns> Returns a token for future connections. </returns> | |||
| /*public async Task<string> ConnectAnonymous(string username) | |||
| { | |||
| await Disconnect(); | |||
| var response = await _api.LoginAnonymous(username); | |||
| if (_isDebugMode) | |||
| RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket got anonymous token."); | |||
| _http.Token = response.Token; | |||
| return await ConnectInternal(response.Token); | |||
| }*/ | |||
| private async Task<string> ConnectInternal(string token) | |||
| { | |||
| _blockEvent.Reset(); | |||
| _http.Token = token; | |||
| string url = (await _api.GetWebSocketEndpoint()).Url; | |||
| string url = (await _api.GetWebSocketEndpoint().ConfigureAwait(false)).Url; | |||
| if (_isDebugMode) | |||
| RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket got endpoint."); | |||
| await _webSocket.ConnectAsync(url); | |||
| await _webSocket.Login(token); | |||
| await _webSocket.ConnectAsync(url).ConfigureAwait(false); | |||
| await _webSocket.Login(token).ConfigureAwait(false); | |||
| _disconnectToken = new CancellationTokenSource(); | |||
| if (_config.UseMessageQueue) | |||
| @@ -550,10 +537,10 @@ namespace Discord | |||
| _mainTask = _disconnectToken.Wait(); | |||
| _mainTask = _mainTask.ContinueWith(async x => | |||
| { | |||
| await _webSocket.DisconnectAsync(); | |||
| await _webSocket.DisconnectAsync().ConfigureAwait(false); | |||
| #if !DNXCORE50 | |||
| if (_config.EnableVoice) | |||
| await _voiceWebSocket.DisconnectAsync(); | |||
| await _voiceWebSocket.DisconnectAsync().ConfigureAwait(false); | |||
| #endif | |||
| //Clear send queue | |||
| @@ -569,7 +556,7 @@ namespace Discord | |||
| _blockEvent.Set(); | |||
| _mainTask = null; | |||
| }).Unwrap(); | |||
| _isConnected = true; | |||
| _isConnected = true; | |||
| return token; | |||
| } | |||
| /// <summary> Disconnects from the Discord server, canceling any pending requests. </summary> | |||
| @@ -578,7 +565,7 @@ namespace Discord | |||
| if (_mainTask != null) | |||
| { | |||
| try { _disconnectToken.Cancel(); } catch (NullReferenceException) { } | |||
| try { await _mainTask; } catch (NullReferenceException) { } | |||
| try { await _mainTask.ConfigureAwait(false); } catch (NullReferenceException) { } | |||
| } | |||
| } | |||
| @@ -591,13 +578,13 @@ namespace Discord | |||
| CheckVoice(); | |||
| if (channel == null) throw new ArgumentNullException(nameof(channel)); | |||
| await LeaveVoiceServer(); | |||
| await LeaveVoiceServer().ConfigureAwait(false); | |||
| //_currentVoiceServerId = channel.ServerId; | |||
| _webSocket.JoinVoice(channel); | |||
| #if !DNXCORE50 | |||
| await _voiceWebSocket.BeginConnect(); | |||
| await _voiceWebSocket.BeginConnect().ConfigureAwait(false); | |||
| #else | |||
| await Task.CompletedTask; | |||
| await Task.CompletedTask.ConfigureAwait(false); | |||
| #endif | |||
| } | |||
| @@ -607,9 +594,9 @@ namespace Discord | |||
| CheckVoice(); | |||
| #if !DNXCORE50 | |||
| await _voiceWebSocket.DisconnectAsync(); | |||
| await _voiceWebSocket.DisconnectAsync().ConfigureAwait(false); | |||
| #else | |||
| await Task.CompletedTask; | |||
| await Task.CompletedTask.ConfigureAwait(false); | |||
| #endif | |||
| //if (_voiceWebSocket.CurrentVoiceServerId != null) | |||
| _webSocket.LeaveVoice(); | |||
| @@ -654,7 +641,7 @@ namespace Discord | |||
| #if !DNXCORE50 | |||
| _voiceWebSocket.Wait(); | |||
| #endif | |||
| await TaskHelper.CompletedTask; | |||
| await TaskHelper.CompletedTask.ConfigureAwait(false); | |||
| } | |||
| //Helpers | |||
| @@ -27,8 +27,8 @@ namespace Discord | |||
| _lastSeq = 0; | |||
| _lastSession = null; | |||
| _redirectServer = null; | |||
| await BeginConnect(); | |||
| await base.ConnectAsync(url); | |||
| await BeginConnect().ConfigureAwait(false); | |||
| await base.ConnectAsync(url).ConfigureAwait(false); | |||
| } | |||
| public async Task Login(string token) | |||
| { | |||
| @@ -44,7 +44,7 @@ namespace Discord | |||
| msg.Payload.Properties["$device"] = "Discord.Net"; | |||
| msg.Payload.Properties["$referrer"] = ""; | |||
| msg.Payload.Properties["$referring_domain"] = ""; | |||
| await SendMessage(msg, cancelToken); | |||
| await SendMessage(msg, cancelToken).ConfigureAwait(false); | |||
| try | |||
| { | |||
| @@ -14,6 +14,7 @@ using System.Threading; | |||
| using System.Threading.Tasks; | |||
| using System.Text; | |||
| using WebSocketMessage = Discord.API.Models.VoiceWebSocketCommands.WebSocketMessage; | |||
| using Discord.Helpers; | |||
| namespace Discord | |||
| { | |||
| @@ -65,29 +66,26 @@ namespace Discord | |||
| _isClearing = false; | |||
| var cancelToken = _disconnectToken.Token; | |||
| Task.Factory.StartNew(async () => | |||
| Task.Run(async () => | |||
| { | |||
| _connectWaitOnLogin.Reset(); | |||
| VoiceWebSocketCommands.Login msg = new VoiceWebSocketCommands.Login(); | |||
| msg.Payload.ServerId = _serverId; | |||
| msg.Payload.SessionId = _sessionId; | |||
| msg.Payload.Token = _token; | |||
| msg.Payload.UserId = _userId; | |||
| await SendMessage(msg, cancelToken); | |||
| try | |||
| { | |||
| _connectWaitOnLogin.Reset(); | |||
| VoiceWebSocketCommands.Login msg = new VoiceWebSocketCommands.Login(); | |||
| msg.Payload.ServerId = _serverId; | |||
| msg.Payload.SessionId = _sessionId; | |||
| msg.Payload.Token = _token; | |||
| msg.Payload.UserId = _userId; | |||
| await SendMessage(msg, cancelToken).ConfigureAwait(false); | |||
| if (!_connectWaitOnLogin.Wait(_timeout, cancelToken)) | |||
| return; | |||
| } | |||
| catch (OperationCanceledException) | |||
| { | |||
| return; | |||
| } | |||
| SetConnected(); | |||
| }); | |||
| SetConnected(); | |||
| } | |||
| catch (OperationCanceledException) { } | |||
| }, _disconnectToken.Token); | |||
| } | |||
| protected override void OnDisconnect() | |||
| { | |||
| @@ -128,41 +126,45 @@ namespace Discord | |||
| public new async Task BeginConnect() | |||
| { | |||
| await base.BeginConnect(); | |||
| await base.BeginConnect().ConfigureAwait(false); | |||
| var cancelToken = _disconnectToken.Token; | |||
| await Task.Yield(); | |||
| try | |||
| { | |||
| if (!_connectWaitOnLogin.Wait(_timeout, cancelToken)) //Waiting on JoinServer message | |||
| throw new Exception("No reply from Discord server"); | |||
| } | |||
| catch (OperationCanceledException) | |||
| await Task.Factory.StartNew(() => | |||
| { | |||
| if (_disconnectReason == null) | |||
| throw new Exception("An unknown websocket error occurred."); | |||
| else | |||
| _disconnectReason.Throw(); | |||
| } | |||
| try | |||
| { | |||
| if (!_connectWaitOnLogin.Wait(_timeout, cancelToken)) //Waiting on JoinServer message | |||
| throw new Exception("No reply from Discord server"); | |||
| } | |||
| catch (OperationCanceledException) | |||
| { | |||
| if (_disconnectReason == null) | |||
| throw new Exception("An unknown websocket error occurred."); | |||
| else | |||
| _disconnectReason.Throw(); | |||
| } | |||
| }).ConfigureAwait(false); | |||
| } | |||
| private async Task ReceiveVoiceAsync() | |||
| { | |||
| var cancelSource = _disconnectToken; | |||
| var cancelToken = cancelSource.Token; | |||
| await Task.Yield(); | |||
| try | |||
| await Task.Run(async () => | |||
| { | |||
| while (!cancelToken.IsCancellationRequested) | |||
| try | |||
| { | |||
| var result = await _udp.ReceiveAsync(); | |||
| ProcessUdpMessage(result); | |||
| while (!cancelToken.IsCancellationRequested) | |||
| { | |||
| var result = await _udp.ReceiveAsync().ConfigureAwait(false); | |||
| ProcessUdpMessage(result); | |||
| } | |||
| } | |||
| } | |||
| catch (OperationCanceledException) { } | |||
| catch (ObjectDisposedException) { } | |||
| catch (Exception ex) { DisconnectInternal(ex); } | |||
| catch (OperationCanceledException) { } | |||
| catch (ObjectDisposedException) { } | |||
| catch (Exception ex) { DisconnectInternal(ex); } | |||
| }).ConfigureAwait(false); | |||
| } | |||
| #if USE_THREAD | |||
| @@ -170,11 +172,12 @@ namespace Discord | |||
| { | |||
| var cancelToken = cancelSource.Token; | |||
| #else | |||
| private async Task SendVoiceAsync() | |||
| private Task SendVoiceAsync() | |||
| { | |||
| var cancelSource = _disconnectToken; | |||
| var cancelToken = cancelSource.Token; | |||
| await Task.Yield(); | |||
| return Task.Run(() => | |||
| { | |||
| #endif | |||
| byte[] packet; | |||
| @@ -224,7 +227,7 @@ namespace Discord | |||
| #if USE_THREAD | |||
| _udp.Send(rtpPacket, packet.Length + 12); | |||
| #else | |||
| await _udp.SendAsync(rtpPacket, packet.Length + 12); | |||
| await _udp.SendAsync(rtpPacket, packet.Length + 12).ConfigureAwait(false); | |||
| #endif | |||
| } | |||
| timestamp = unchecked(timestamp + samplesPerFrame); | |||
| @@ -247,24 +250,23 @@ namespace Discord | |||
| #if USE_THREAD | |||
| Thread.Sleep(1); | |||
| #else | |||
| await Task.Delay(1); | |||
| await Task.Delay(1).ConfigureAwait(false); | |||
| #endif | |||
| } | |||
| } | |||
| catch (OperationCanceledException) { } | |||
| catch (ObjectDisposedException) { } | |||
| catch (Exception ex) { DisconnectInternal(ex); } | |||
| } | |||
| //Closes the UDP socket when _disconnectToken is triggered, since UDPClient doesn't allow passing a canceltoken | |||
| private async Task WatcherAsync() | |||
| #if !USE_THREAD | |||
| }).ConfigureAwait(false); | |||
| #endif | |||
| } | |||
| //Closes the UDP socket when _disconnectToken is triggered, since UDPClient doesn't allow passing a canceltoken | |||
| private Task WatcherAsync() | |||
| { | |||
| var cancelToken = _disconnectToken.Token; | |||
| try | |||
| { | |||
| await Task.Delay(-1, cancelToken); | |||
| } | |||
| catch (OperationCanceledException) { } | |||
| finally { _udp.Close(); } | |||
| return cancelToken.Wait() | |||
| .ContinueWith(_ => _udp.Close()); | |||
| } | |||
| protected override async Task ProcessMessage(string json) | |||
| @@ -279,7 +281,7 @@ namespace Discord | |||
| var payload = (msg.Payload as JToken).ToObject<VoiceWebSocketEvents.Ready>(); | |||
| _heartbeatInterval = payload.HeartbeatInterval; | |||
| _ssrc = payload.SSRC; | |||
| _endpoint = new IPEndPoint((await Dns.GetHostAddressesAsync(_host.Replace("wss://", ""))).FirstOrDefault(), payload.Port); | |||
| _endpoint = new IPEndPoint((await Dns.GetHostAddressesAsync(_host.Replace("wss://", "")).ConfigureAwait(false)).FirstOrDefault(), payload.Port); | |||
| //_mode = payload.Modes.LastOrDefault(); | |||
| _mode = "plain"; | |||
| _udp.Connect(_endpoint); | |||
| @@ -295,7 +297,7 @@ namespace Discord | |||
| 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | |||
| 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | |||
| 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | |||
| 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, 70); | |||
| 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, 70).ConfigureAwait(false); | |||
| } | |||
| } | |||
| break; | |||
| @@ -42,19 +42,17 @@ namespace Discord | |||
| protected virtual async Task BeginConnect() | |||
| { | |||
| await DisconnectAsync(); | |||
| await DisconnectAsync().ConfigureAwait(false); | |||
| _disconnectToken = new CancellationTokenSource(); | |||
| _disconnectReason = null; | |||
| } | |||
| public virtual async Task ConnectAsync(string url) | |||
| { | |||
| //await DisconnectAsync(); | |||
| var cancelToken = _disconnectToken.Token; | |||
| _webSocket = new ClientWebSocket(); | |||
| _webSocket.Options.KeepAliveInterval = TimeSpan.Zero; | |||
| await _webSocket.ConnectAsync(new Uri(url), cancelToken); | |||
| await _webSocket.ConnectAsync(new Uri(url), cancelToken).ConfigureAwait(false); | |||
| _host = url; | |||
| if (_isDebug) | |||
| @@ -99,7 +97,7 @@ namespace Discord | |||
| if (_task != null) | |||
| { | |||
| try { DisconnectInternal(new Exception("Disconnect requested by user."), false); } catch (NullReferenceException) { } | |||
| try { await _task; } catch (NullReferenceException) { } | |||
| try { await _task.ConfigureAwait(false); } catch (NullReferenceException) { } | |||
| } | |||
| } | |||
| @@ -131,90 +129,94 @@ namespace Discord | |||
| }; | |||
| } | |||
| private async Task ReceiveAsync() | |||
| private Task ReceiveAsync() | |||
| { | |||
| var cancelSource = _disconnectToken; | |||
| var cancelToken = cancelSource.Token; | |||
| await Task.Yield(); | |||
| var buffer = new byte[ReceiveChunkSize]; | |||
| var builder = new StringBuilder(); | |||
| try | |||
| return Task.Run(async () => | |||
| { | |||
| while (_webSocket.State == WebSocketState.Open && !cancelToken.IsCancellationRequested) | |||
| var buffer = new byte[ReceiveChunkSize]; | |||
| var builder = new StringBuilder(); | |||
| try | |||
| { | |||
| WebSocketReceiveResult result = null; | |||
| do | |||
| while (_webSocket.State == WebSocketState.Open && !cancelToken.IsCancellationRequested) | |||
| { | |||
| if (_webSocket.State != WebSocketState.Open || cancelToken.IsCancellationRequested) | |||
| return; | |||
| try | |||
| WebSocketReceiveResult result = null; | |||
| do | |||
| { | |||
| result = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), cancelToken); | |||
| } | |||
| catch (Win32Exception ex) | |||
| when (ex.HResult == HR_TIMEOUT) | |||
| { | |||
| string msg = $"Connection timed out."; | |||
| RaiseOnDebugMessage(DebugMessageType.Connection, msg); | |||
| DisconnectInternal(new Exception(msg)); | |||
| return; | |||
| } | |||
| if (_webSocket.State != WebSocketState.Open || cancelToken.IsCancellationRequested) | |||
| return; | |||
| try | |||
| { | |||
| result = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), cancelToken).ConfigureAwait(false); | |||
| } | |||
| catch (Win32Exception ex) | |||
| when (ex.HResult == HR_TIMEOUT) | |||
| { | |||
| string msg = $"Connection timed out."; | |||
| RaiseOnDebugMessage(DebugMessageType.Connection, msg); | |||
| DisconnectInternal(new Exception(msg)); | |||
| return; | |||
| } | |||
| if (result.MessageType == WebSocketMessageType.Close) | |||
| { | |||
| string msg = $"Got Close Message ({result.CloseStatus?.ToString() ?? "Unexpected"}, {result.CloseStatusDescription ?? "No Reason"})"; | |||
| RaiseOnDebugMessage(DebugMessageType.Connection, msg); | |||
| DisconnectInternal(new Exception(msg)); | |||
| await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).ConfigureAwait(false); | |||
| return; | |||
| } | |||
| else | |||
| builder.Append(Encoding.UTF8.GetString(buffer, 0, result.Count)); | |||
| if (result.MessageType == WebSocketMessageType.Close) | |||
| { | |||
| string msg = $"Got Close Message ({result.CloseStatus?.ToString() ?? "Unexpected"}, {result.CloseStatusDescription ?? "No Reason"})"; | |||
| RaiseOnDebugMessage(DebugMessageType.Connection, msg); | |||
| DisconnectInternal(new Exception(msg)); | |||
| await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); | |||
| return; | |||
| } | |||
| else | |||
| builder.Append(Encoding.UTF8.GetString(buffer, 0, result.Count)); | |||
| } | |||
| while (result == null || !result.EndOfMessage); | |||
| while (result == null || !result.EndOfMessage); | |||
| #if DEBUG | |||
| System.Diagnostics.Debug.WriteLine(">>> " + builder.ToString()); | |||
| System.Diagnostics.Debug.WriteLine(">>> " + builder.ToString()); | |||
| #endif | |||
| await ProcessMessage(builder.ToString()); | |||
| await ProcessMessage(builder.ToString()).ConfigureAwait(false); | |||
| builder.Clear(); | |||
| builder.Clear(); | |||
| } | |||
| } | |||
| } | |||
| catch (OperationCanceledException) { } | |||
| catch (Exception ex) { DisconnectInternal(ex); } | |||
| } | |||
| private async Task SendAsync() | |||
| catch (OperationCanceledException) { } | |||
| catch (Exception ex) { DisconnectInternal(ex); } | |||
| }); | |||
| } | |||
| private Task SendAsync() | |||
| { | |||
| var cancelSource = _disconnectToken; | |||
| var cancelToken = cancelSource.Token; | |||
| await Task.Yield(); | |||
| try | |||
| return Task.Run(async () => | |||
| { | |||
| byte[] bytes; | |||
| while (_webSocket.State == WebSocketState.Open && !cancelToken.IsCancellationRequested) | |||
| try | |||
| { | |||
| if (_heartbeatInterval > 0) | |||
| byte[] bytes; | |||
| while (_webSocket.State == WebSocketState.Open && !cancelToken.IsCancellationRequested) | |||
| { | |||
| DateTime now = DateTime.UtcNow; | |||
| if ((now - _lastHeartbeat).TotalMilliseconds > _heartbeatInterval) | |||
| if (_heartbeatInterval > 0) | |||
| { | |||
| await SendMessage(GetKeepAlive(), cancelToken); | |||
| _lastHeartbeat = now; | |||
| DateTime now = DateTime.UtcNow; | |||
| if ((now - _lastHeartbeat).TotalMilliseconds > _heartbeatInterval) | |||
| { | |||
| await SendMessage(GetKeepAlive(), cancelToken).ConfigureAwait(false); | |||
| _lastHeartbeat = now; | |||
| } | |||
| } | |||
| while (_sendQueue.TryDequeue(out bytes)) | |||
| await SendMessage(bytes, cancelToken).ConfigureAwait(false); | |||
| await Task.Delay(_sendInterval, cancelToken).ConfigureAwait(false); | |||
| } | |||
| while (_sendQueue.TryDequeue(out bytes)) | |||
| await SendMessage(bytes, cancelToken); | |||
| await Task.Delay(_sendInterval, cancelToken); | |||
| } | |||
| } | |||
| catch (OperationCanceledException) { } | |||
| catch (Exception ex) { DisconnectInternal(ex); } | |||
| catch (OperationCanceledException) { } | |||
| catch (Exception ex) { DisconnectInternal(ex); } | |||
| }); | |||
| } | |||
| protected abstract Task ProcessMessage(string json); | |||
| @@ -252,7 +254,7 @@ namespace Discord | |||
| try | |||
| { | |||
| await _webSocket.SendAsync(new ArraySegment<byte>(message, offset, count), WebSocketMessageType.Text, isLast, cancelToken); | |||
| await _webSocket.SendAsync(new ArraySegment<byte>(message, offset, count), WebSocketMessageType.Text, isLast, cancelToken).ConfigureAwait(false); | |||
| } | |||
| catch (Win32Exception ex) | |||
| when (ex.HResult == HR_TIMEOUT) | |||
| @@ -9,12 +9,12 @@ namespace Discord.Helpers | |||
| public static async Task Wait(this CancellationTokenSource tokenSource) | |||
| { | |||
| var token = tokenSource.Token; | |||
| try { await Task.Delay(-1, token); } | |||
| try { await Task.Delay(-1, token).ConfigureAwait(false); } | |||
| catch (OperationCanceledException) { } | |||
| } | |||
| public static async Task Wait(this CancellationToken token) | |||
| { | |||
| try { await Task.Delay(-1, token); } | |||
| try { await Task.Delay(-1, token).ConfigureAwait(false); } | |||
| catch (OperationCanceledException) { } | |||
| } | |||
| } | |||
| @@ -117,7 +117,7 @@ namespace Discord.Helpers | |||
| private async Task<ResponseT> Send<ResponseT>(HttpMethod method, string path, HttpContent content) | |||
| where ResponseT : class | |||
| { | |||
| string responseJson = await SendRequest(method, path, content, true); | |||
| string responseJson = await SendRequest(method, path, content, true).ConfigureAwait(false); | |||
| #if TEST_RESPONSES | |||
| if (path.StartsWith(Endpoints.BaseApi)) | |||
| return JsonConvert.DeserializeObject<ResponseT>(responseJson, _settings); | |||
| @@ -127,7 +127,7 @@ namespace Discord.Helpers | |||
| #if TEST_RESPONSES | |||
| private async Task<string> Send(HttpMethod method, string path, HttpContent content) | |||
| { | |||
| string responseJson = await SendRequest(method, path, content, true); | |||
| string responseJson = await SendRequest(method, path, content, true).ConfigureAwait(false); | |||
| if (path.StartsWith(Endpoints.BaseApi) && !string.IsNullOrEmpty(responseJson)) | |||
| throw new Exception("API check failed: Response is not empty."); | |||
| return responseJson; | |||
| @@ -146,12 +146,12 @@ namespace Discord.Helpers | |||
| { | |||
| if (content is StringContent) | |||
| { | |||
| string json = await (content as StringContent).ReadAsStringAsync(); | |||
| string json = await (content as StringContent).ReadAsStringAsync().ConfigureAwait(false); | |||
| RaiseOnDebugMessage(DebugMessageType.XHRRawOutput, $"{method} {path}: {json}"); | |||
| } | |||
| else | |||
| { | |||
| byte[] bytes = await content.ReadAsByteArrayAsync(); | |||
| byte[] bytes = await content.ReadAsByteArrayAsync().ConfigureAwait(false); | |||
| RaiseOnDebugMessage(DebugMessageType.XHRRawOutput, $"{method} {path}: {bytes.Length} bytes"); | |||
| } | |||
| } | |||
| @@ -167,14 +167,14 @@ namespace Discord.Helpers | |||
| HttpResponseMessage response; | |||
| if (hasResponse) | |||
| { | |||
| response = await _client.SendAsync(msg, HttpCompletionOption.ResponseContentRead); | |||
| response = await _client.SendAsync(msg, HttpCompletionOption.ResponseContentRead).ConfigureAwait(false); | |||
| if (!response.IsSuccessStatusCode) | |||
| throw new HttpException(response.StatusCode); | |||
| result = await response.Content.ReadAsStringAsync(); | |||
| result = await response.Content.ReadAsStringAsync().ConfigureAwait(false); | |||
| } | |||
| else | |||
| { | |||
| response = await _client.SendAsync(msg, HttpCompletionOption.ResponseHeadersRead); | |||
| response = await _client.SendAsync(msg, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false); | |||
| if (!response.IsSuccessStatusCode) | |||
| throw new HttpException(response.StatusCode); | |||
| result = null; | |||