This commit is contained in:
2021-08-01 17:32:43 -05:00
parent 178cb00253
commit b10f61a489
76 changed files with 1655 additions and 1185 deletions

View File

@@ -1,6 +1,6 @@
using FluentValidation;
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket.Messages;
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket.Types;
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket;
using Gameboard.ShogiUI.Sockets.ServiceModels.Types;
namespace Gameboard.ShogiUI.Sockets.Services.RequestValidators
{

View File

@@ -1,6 +1,6 @@
using FluentValidation;
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket.Messages;
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket.Types;
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket;
using Gameboard.ShogiUI.Sockets.ServiceModels.Types;
namespace Gameboard.ShogiUI.Sockets.Services.RequestValidators
{

View File

@@ -1,6 +1,6 @@
using FluentValidation;
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket.Messages;
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket.Types;
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket;
using Gameboard.ShogiUI.Sockets.ServiceModels.Types;
namespace Gameboard.ShogiUI.Sockets.Services.RequestValidators
{

View File

@@ -1,6 +1,6 @@
using FluentValidation;
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket.Messages;
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket.Types;
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket;
using Gameboard.ShogiUI.Sockets.ServiceModels.Types;
namespace Gameboard.ShogiUI.Sockets.Services.RequestValidators
{

View File

@@ -1,6 +1,6 @@
using FluentValidation;
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket.Messages;
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket.Types;
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket;
using Gameboard.ShogiUI.Sockets.ServiceModels.Types;
namespace Gameboard.ShogiUI.Sockets.Services.RequestValidators
{

View File

@@ -1,6 +1,6 @@
using FluentValidation;
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket.Messages;
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket.Types;
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket;
using Gameboard.ShogiUI.Sockets.ServiceModels.Types;
namespace Gameboard.ShogiUI.Sockets.Services.RequestValidators
{

View File

@@ -1,11 +1,12 @@
using FluentValidation;
using Gameboard.ShogiUI.Sockets.Controllers;
using Gameboard.ShogiUI.Sockets.Extensions;
using Gameboard.ShogiUI.Sockets.Managers;
using Gameboard.ShogiUI.Sockets.Managers.ClientActionHandlers;
using Gameboard.ShogiUI.Sockets.Managers.Utility;
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket.Interfaces;
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket.Messages;
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket.Types;
using Gameboard.ShogiUI.Sockets.Repositories;
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket;
using Gameboard.ShogiUI.Sockets.ServiceModels.Types;
using Gameboard.ShogiUI.Sockets.Services.Utility;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
@@ -29,6 +30,7 @@ namespace Gameboard.ShogiUI.Sockets.Services
{
private readonly ILogger<SocketService> logger;
private readonly ISocketConnectionManager communicationManager;
private readonly IGameboardRepository gameboardRepository;
private readonly ISocketTokenManager tokenManager;
private readonly ICreateGameHandler createGameHandler;
private readonly IJoinByCodeHandler joinByCodeHandler;
@@ -46,6 +48,7 @@ namespace Gameboard.ShogiUI.Sockets.Services
public SocketService(
ILogger<SocketService> logger,
ISocketConnectionManager communicationManager,
IGameboardRepository gameboardRepository,
ISocketTokenManager tokenManager,
ICreateGameHandler createGameHandler,
IJoinByCodeHandler joinByCodeHandler,
@@ -63,6 +66,7 @@ namespace Gameboard.ShogiUI.Sockets.Services
{
this.logger = logger;
this.communicationManager = communicationManager;
this.gameboardRepository = gameboardRepository;
this.tokenManager = tokenManager;
this.createGameHandler = createGameHandler;
this.joinByCodeHandler = joinByCodeHandler;
@@ -80,105 +84,115 @@ namespace Gameboard.ShogiUI.Sockets.Services
public async Task HandleSocketRequest(HttpContext context)
{
var hasToken = context.Request.Query.Keys.Contains("token");
if (hasToken)
string? userName = null;
if (context.Request.Cookies.ContainsKey(SocketController.WebSessionKey))
{
var oneTimeToken = context.Request.Query["token"][0];
var tokenAsGuid = Guid.Parse(oneTimeToken);
var userName = tokenManager.GetUsername(tokenAsGuid);
if (userName != null)
{
var socket = await context.WebSockets.AcceptWebSocketAsync();
// Guest account
var webSessionId = Guid.Parse(context.Request.Cookies[SocketController.WebSessionKey]!);
userName = (await gameboardRepository.ReadGuestUser(webSessionId))?.Name;
}
else if (context.Request.Query.Keys.Contains("token"))
{
// Microsoft account
var token = Guid.Parse(context.Request.Query["token"][0]);
userName = tokenManager.GetUsername(token);
}
communicationManager.SubscribeToBroadcast(socket, userName);
while (socket.State == WebSocketState.Open)
if (string.IsNullOrEmpty(userName))
{
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
return;
}
else
{
var socket = await context.WebSockets.AcceptWebSocketAsync();
communicationManager.SubscribeToBroadcast(socket, userName);
while (socket.State == WebSocketState.Open)
{
try
{
try
var message = await socket.ReceiveTextAsync();
if (string.IsNullOrWhiteSpace(message)) continue;
logger.LogInformation("Request \n{0}\n", message);
var request = JsonConvert.DeserializeObject<Request>(message);
if (!Enum.IsDefined(typeof(ClientAction), request.Action))
{
var message = await socket.ReceiveTextAsync();
if (string.IsNullOrWhiteSpace(message)) continue;
logger.LogInformation("Request \n{0}\n", message);
var request = JsonConvert.DeserializeObject<Request>(message);
if (!Enum.IsDefined(typeof(ClientAction), request.Action))
{
await socket.SendTextAsync("Error: Action not recognized.");
continue;
}
switch (request.Action)
{
case ClientAction.ListGames:
{
var req = JsonConvert.DeserializeObject<ListGamesRequest>(message);
if (await ValidateRequestAndReplyIfInvalid(socket, listGamesRequestValidator, req))
{
await listGamesHandler.Handle(req, userName);
}
break;
}
case ClientAction.CreateGame:
{
var req = JsonConvert.DeserializeObject<CreateGameRequest>(message);
if (await ValidateRequestAndReplyIfInvalid(socket, createGameRequestValidator, req))
{
await createGameHandler.Handle(req, userName);
}
break;
}
case ClientAction.JoinGame:
{
var req = JsonConvert.DeserializeObject<JoinGameRequest>(message);
if (await ValidateRequestAndReplyIfInvalid(socket, joinGameRequestValidator, req))
{
await joinGameHandler.Handle(req, userName);
}
break;
}
case ClientAction.JoinByCode:
{
var req = JsonConvert.DeserializeObject<JoinByCodeRequest>(message);
if (await ValidateRequestAndReplyIfInvalid(socket, joinByCodeRequestValidator, req))
{
await joinByCodeHandler.Handle(req, userName);
}
break;
}
case ClientAction.LoadGame:
{
var req = JsonConvert.DeserializeObject<LoadGameRequest>(message);
if (await ValidateRequestAndReplyIfInvalid(socket, loadGameRequestValidator, req))
{
await loadGameHandler.Handle(req, userName);
}
break;
}
case ClientAction.Move:
{
var req = JsonConvert.DeserializeObject<MoveRequest>(message);
if (await ValidateRequestAndReplyIfInvalid(socket, moveRequestValidator, req))
{
await moveHandler.Handle(req, userName);
}
break;
}
}
await socket.SendTextAsync("Error: Action not recognized.");
continue;
}
catch (OperationCanceledException ex)
switch (request.Action)
{
logger.LogError(ex.Message);
}
catch (WebSocketException ex)
{
logger.LogInformation($"{nameof(WebSocketException)} in {nameof(SocketConnectionManager)}.");
logger.LogInformation("Probably tried writing to a closed socket.");
logger.LogError(ex.Message);
case ClientAction.ListGames:
{
var req = JsonConvert.DeserializeObject<ListGamesRequest>(message);
if (await ValidateRequestAndReplyIfInvalid(socket, listGamesRequestValidator, req))
{
await listGamesHandler.Handle(req, userName);
}
break;
}
case ClientAction.CreateGame:
{
var req = JsonConvert.DeserializeObject<CreateGameRequest>(message);
if (await ValidateRequestAndReplyIfInvalid(socket, createGameRequestValidator, req))
{
await createGameHandler.Handle(req, userName);
}
break;
}
case ClientAction.JoinGame:
{
var req = JsonConvert.DeserializeObject<JoinGameRequest>(message);
if (await ValidateRequestAndReplyIfInvalid(socket, joinGameRequestValidator, req))
{
await joinGameHandler.Handle(req, userName);
}
break;
}
case ClientAction.JoinByCode:
{
var req = JsonConvert.DeserializeObject<JoinByCodeRequest>(message);
if (await ValidateRequestAndReplyIfInvalid(socket, joinByCodeRequestValidator, req))
{
await joinByCodeHandler.Handle(req, userName);
}
break;
}
case ClientAction.LoadGame:
{
var req = JsonConvert.DeserializeObject<LoadGameRequest>(message);
if (await ValidateRequestAndReplyIfInvalid(socket, loadGameRequestValidator, req))
{
await loadGameHandler.Handle(req, userName);
}
break;
}
case ClientAction.Move:
{
var req = JsonConvert.DeserializeObject<MoveRequest>(message);
if (await ValidateRequestAndReplyIfInvalid(socket, moveRequestValidator, req))
{
await moveHandler.Handle(req, userName);
}
break;
}
}
}
communicationManager.UnsubscribeFromBroadcastAndGames(userName);
return;
catch (OperationCanceledException ex)
{
logger.LogError(ex.Message);
}
catch (WebSocketException ex)
{
logger.LogInformation($"{nameof(WebSocketException)} in {nameof(SocketConnectionManager)}.");
logger.LogInformation("Probably tried writing to a closed socket.");
logger.LogError(ex.Message);
}
}
communicationManager.UnsubscribeFromBroadcastAndGames(userName);
return;
}
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
return;
}
public async Task<bool> ValidateRequestAndReplyIfInvalid<TRequest>(WebSocket socket, IValidator<TRequest> validator, TRequest request)
@@ -186,7 +200,12 @@ namespace Gameboard.ShogiUI.Sockets.Services
var results = validator.Validate(request);
if (!results.IsValid)
{
await socket.SendTextAsync(string.Join('\n', results.Errors.Select(_ => _.ErrorMessage).ToString()));
var errors = string.Join('\n', results.Errors.Select(_ => _.ErrorMessage));
var message = JsonConvert.SerializeObject(new Response
{
Error = errors
});
await socket.SendTextAsync(message);
}
return results.IsValid;
}

View File

@@ -0,0 +1,15 @@
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket;
namespace Gameboard.ShogiUI.Sockets.Services.Utility
{
public class JsonRequest
{
public IRequest Request { get; private set; }
public string Json { get; private set; }
public JsonRequest(IRequest request, string json)
{
Request = request;
Json = json;
}
}
}

View File

@@ -0,0 +1,10 @@
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket;
using Gameboard.ShogiUI.Sockets.ServiceModels.Types;
namespace Gameboard.ShogiUI.Sockets.Services.Utility
{
public class Request : IRequest
{
public ClientAction Action { get; set; }
}
}

View File

@@ -0,0 +1,10 @@
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket;
namespace Gameboard.ShogiUI.Sockets.Services.Utility
{
public class Response : IResponse
{
public string Action { get; set; }
public string Error { get; set; }
}
}