@@ -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; | |||