Fix fire'n'forget task from blocking guest logins.
This commit is contained in:
@@ -45,8 +45,13 @@ public class AccountManager
|
|||||||
Id = response.UserId,
|
Id = response.UserId,
|
||||||
WhichAccountPlatform = WhichAccountPlatform.Guest
|
WhichAccountPlatform = WhichAccountPlatform.Guest
|
||||||
});
|
});
|
||||||
await shogiSocket.OpenAsync(response.OneTimeToken.ToString());
|
|
||||||
await localStorage.SetAccountPlatform(WhichAccountPlatform.Guest);
|
await localStorage.SetAccountPlatform(WhichAccountPlatform.Guest);
|
||||||
|
// TODO: OpenAsync() sometimes doesn't return, probably because of the fire'n'forget task inside it. Figure that out.
|
||||||
|
await shogiSocket.OpenAsync(response.OneTimeToken.ToString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Failed to get token from server during guest login.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,16 +119,19 @@ public class AccountManager
|
|||||||
{
|
{
|
||||||
var platform = await localStorage.GetAccountPlatform();
|
var platform = await localStorage.GetAccountPlatform();
|
||||||
await localStorage.DeleteAccountPlatform();
|
await localStorage.DeleteAccountPlatform();
|
||||||
|
await accountState.SetUser(null);
|
||||||
|
|
||||||
if (platform == WhichAccountPlatform.Guest)
|
if (platform == WhichAccountPlatform.Guest)
|
||||||
{
|
{
|
||||||
await shogiApi.GuestLogout();
|
await shogiApi.GuestLogout();
|
||||||
await accountState.SetUser(null);
|
|
||||||
}
|
}
|
||||||
else if (platform == WhichAccountPlatform.Microsoft)
|
else if (platform == WhichAccountPlatform.Microsoft)
|
||||||
{
|
{
|
||||||
await accountState.SetUser(null);
|
|
||||||
navigation.NavigateToLogout("authentication/logout");
|
navigation.NavigateToLogout("authentication/logout");
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Tried to logout without a valid account platform.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,29 +11,32 @@ namespace Shogi.UI.Pages.Home.Api
|
|||||||
{
|
{
|
||||||
public const string GuestClientName = "Guest";
|
public const string GuestClientName = "Guest";
|
||||||
public const string MsalClientName = "Msal";
|
public const string MsalClientName = "Msal";
|
||||||
//public const string AnonymousClientName = "Anonymous";
|
|
||||||
|
|
||||||
private readonly JsonSerializerOptions serializerOptions;
|
private readonly JsonSerializerOptions serializerOptions;
|
||||||
private readonly IHttpClientFactory clientFactory;
|
private readonly IHttpClientFactory clientFactory;
|
||||||
private readonly AccountState accountState;
|
private readonly AccountState accountState;
|
||||||
|
private readonly HttpClient guestHttpClient;
|
||||||
|
private readonly HttpClient msalHttpClient;
|
||||||
|
|
||||||
public ShogiApi(IHttpClientFactory clientFactory, AccountState accountState)
|
public ShogiApi(IHttpClientFactory clientFactory, AccountState accountState)
|
||||||
{
|
{
|
||||||
serializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web);
|
serializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web);
|
||||||
this.clientFactory = clientFactory;
|
this.clientFactory = clientFactory;
|
||||||
this.accountState = accountState;
|
this.accountState = accountState;
|
||||||
|
this.guestHttpClient = clientFactory.CreateClient(GuestClientName);
|
||||||
|
this.msalHttpClient = clientFactory.CreateClient(MsalClientName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private HttpClient HttpClient => accountState.User?.WhichAccountPlatform switch
|
private HttpClient HttpClient => accountState.User?.WhichAccountPlatform switch
|
||||||
{
|
{
|
||||||
WhichAccountPlatform.Guest => clientFactory.CreateClient(GuestClientName),
|
WhichAccountPlatform.Guest => this.guestHttpClient,
|
||||||
WhichAccountPlatform.Microsoft => clientFactory.CreateClient(MsalClientName),
|
WhichAccountPlatform.Microsoft => this.msalHttpClient,
|
||||||
_ => throw new InvalidOperationException("AccountState.User must not be null during API call.")
|
_ => throw new InvalidOperationException("AccountState.User must not be null during API call.")
|
||||||
};
|
};
|
||||||
|
|
||||||
public async Task GuestLogout()
|
public async Task GuestLogout()
|
||||||
{
|
{
|
||||||
var response = await HttpClient.PutAsync(new Uri("User/GuestLogout", UriKind.Relative), null);
|
var response = await this.guestHttpClient.PutAsync(new Uri("User/GuestLogout", UriKind.Relative), null);
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,8 +66,8 @@ namespace Shogi.UI.Pages.Home.Api
|
|||||||
public async Task<CreateTokenResponse?> GetToken(WhichAccountPlatform whichAccountPlatform)
|
public async Task<CreateTokenResponse?> GetToken(WhichAccountPlatform whichAccountPlatform)
|
||||||
{
|
{
|
||||||
var httpClient = whichAccountPlatform == WhichAccountPlatform.Microsoft
|
var httpClient = whichAccountPlatform == WhichAccountPlatform.Microsoft
|
||||||
? clientFactory.CreateClient(MsalClientName)
|
? this.msalHttpClient
|
||||||
: clientFactory.CreateClient(GuestClientName);
|
: this.guestHttpClient;
|
||||||
var response = await httpClient.GetAsync(RelativeUri("User/Token"));
|
var response = await httpClient.GetAsync(RelativeUri("User/Token"));
|
||||||
if (response.IsSuccessStatusCode)
|
if (response.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ static void ConfigureDependencies(IServiceCollection services, IConfiguration co
|
|||||||
});
|
});
|
||||||
|
|
||||||
// https://docs.microsoft.com/en-us/aspnet/core/blazor/fundamentals/dependency-injection?view=aspnetcore-6.0#service-lifetime
|
// https://docs.microsoft.com/en-us/aspnet/core/blazor/fundamentals/dependency-injection?view=aspnetcore-6.0#service-lifetime
|
||||||
services.AddScoped<ClientWebSocket>();
|
|
||||||
services.AddScoped<AccountManager>();
|
services.AddScoped<AccountManager>();
|
||||||
services.AddScoped<AccountState>();
|
services.AddScoped<AccountState>();
|
||||||
services.AddScoped<ShogiSocket>();
|
services.AddScoped<ShogiSocket>();
|
||||||
|
|||||||
@@ -14,16 +14,16 @@ public class ShogiSocket : IDisposable
|
|||||||
public event AsyncEventHandler<SessionJoinedByPlayerSocketMessage>? OnSessionJoined;
|
public event AsyncEventHandler<SessionJoinedByPlayerSocketMessage>? OnSessionJoined;
|
||||||
public event AsyncEventHandler<PlayerHasMovedMessage>? OnPlayerMoved;
|
public event AsyncEventHandler<PlayerHasMovedMessage>? OnPlayerMoved;
|
||||||
|
|
||||||
private readonly ClientWebSocket socket;
|
private ClientWebSocket socket;
|
||||||
private readonly JsonSerializerOptions serializerOptions;
|
private readonly JsonSerializerOptions serializerOptions;
|
||||||
private readonly UriBuilder uriBuilder;
|
private readonly UriBuilder uriBuilder;
|
||||||
private readonly CancellationTokenSource cancelToken;
|
private readonly CancellationTokenSource cancelToken;
|
||||||
private readonly IMemoryOwner<byte> memoryOwner;
|
private readonly IMemoryOwner<byte> memoryOwner;
|
||||||
private bool disposedValue;
|
private bool disposedValue;
|
||||||
|
|
||||||
public ShogiSocket(IConfiguration configuration, ClientWebSocket socket, JsonSerializerOptions serializerOptions)
|
public ShogiSocket(IConfiguration configuration, JsonSerializerOptions serializerOptions)
|
||||||
{
|
{
|
||||||
this.socket = socket;
|
this.socket = new ClientWebSocket();
|
||||||
this.serializerOptions = serializerOptions;
|
this.serializerOptions = serializerOptions;
|
||||||
this.uriBuilder = new UriBuilder(configuration["SocketUrl"] ?? throw new InvalidOperationException("SocketUrl configuration is missing."));
|
this.uriBuilder = new UriBuilder(configuration["SocketUrl"] ?? throw new InvalidOperationException("SocketUrl configuration is missing."));
|
||||||
this.cancelToken = new CancellationTokenSource();
|
this.cancelToken = new CancellationTokenSource();
|
||||||
@@ -36,22 +36,30 @@ public class ShogiSocket : IDisposable
|
|||||||
{
|
{
|
||||||
await this.socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing before opening a new connection.", CancellationToken.None);
|
await this.socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing before opening a new connection.", CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
if (this.socket.State == WebSocketState.Closed)
|
||||||
|
{
|
||||||
|
this.socket.Dispose();
|
||||||
|
this.socket = new ClientWebSocket(); // Because you can't reopen a closed socket.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("Opening socket and existing socket state is " + this.socket.State.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
uriBuilder.Query = new QueryBuilder { { "token", token } }.ToQueryString().Value;
|
uriBuilder.Query = new QueryBuilder { { "token", token } }.ToQueryString().Value;
|
||||||
|
|
||||||
Console.WriteLine("ShogiSocket.OpenAsync socket state is {0}", this.socket.State.ToString());
|
|
||||||
|
|
||||||
await socket.ConnectAsync(this.uriBuilder.Uri, cancelToken.Token);
|
await socket.ConnectAsync(this.uriBuilder.Uri, cancelToken.Token);
|
||||||
// Fire and forget! I'm way too lazy to write my own javascript interop to a web worker. Nooo thanks.
|
// Fire and forget! I'm way too lazy to write my own javascript interop to a web worker. Nooo thanks.
|
||||||
_ = Listen().ContinueWith(async antecedent =>
|
_ = Listen()
|
||||||
{
|
.ContinueWith(async antecedent =>
|
||||||
this.cancelToken.Cancel();
|
|
||||||
await this.socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Page was probably closed or refresh.", CancellationToken.None);
|
|
||||||
if (antecedent.Exception != null)
|
|
||||||
{
|
{
|
||||||
throw antecedent.Exception;
|
this.cancelToken.Cancel();
|
||||||
}
|
await this.socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Page was probably closed or refresh.", CancellationToken.None);
|
||||||
}, TaskContinuationOptions.OnlyOnFaulted);
|
if (antecedent.Exception != null)
|
||||||
|
{
|
||||||
|
throw antecedent.Exception;
|
||||||
|
}
|
||||||
|
}, TaskContinuationOptions.OnlyOnFaulted)
|
||||||
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Listen()
|
private async Task Listen()
|
||||||
|
|||||||
Reference in New Issue
Block a user