using Gameboard.ShogiUI.Sockets.Extensions; using Gameboard.ShogiUI.Sockets.Managers.ClientActionHandlers; using Gameboard.ShogiUI.Sockets.Managers.Utility; using Gameboard.ShogiUI.Sockets.ServiceModels.Socket.Messages; using Gameboard.ShogiUI.Sockets.ServiceModels.Socket.Types; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using System; using System.Net; using System.Net.WebSockets; using System.Threading.Tasks; namespace Gameboard.ShogiUI.Sockets.Managers { public interface ISocketConnectionManager { Task HandleSocketRequest(HttpContext context); } public class SocketConnectionManager : ISocketConnectionManager { private readonly ILogger logger; private readonly ISocketCommunicationManager communicationManager; private readonly ISocketTokenManager tokenManager; private readonly ICreateGameHandler createGameHandler; private readonly IJoinByCodeHandler joinByCodeHandler; private readonly IJoinGameHandler joinGameHandler; private readonly IListGamesHandler listGamesHandler; private readonly ILoadGameHandler loadGameHandler; private readonly IMoveHandler moveHandler; public SocketConnectionManager( ILogger logger, ISocketCommunicationManager communicationManager, ISocketTokenManager tokenManager, ICreateGameHandler createGameHandler, IJoinByCodeHandler joinByCodeHandler, IJoinGameHandler joinGameHandler, IListGamesHandler listGamesHandler, ILoadGameHandler loadGameHandler, IMoveHandler moveHandler) : base() { this.logger = logger; this.communicationManager = communicationManager; this.tokenManager = tokenManager; this.createGameHandler = createGameHandler; this.joinByCodeHandler = joinByCodeHandler; this.joinGameHandler = joinGameHandler; this.listGamesHandler = listGamesHandler; this.loadGameHandler = loadGameHandler; this.moveHandler = moveHandler; } public async Task HandleSocketRequest(HttpContext context) { var hasToken = context.Request.Query.Keys.Contains("token"); if (hasToken) { 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(); communicationManager.SubscribeToBroadcast(socket, userName); while (!socket.CloseStatus.HasValue) { try { var message = await socket.ReceiveTextAsync(); if (string.IsNullOrWhiteSpace(message)) continue; logger.LogInformation("Request \n{0}\n", message); var request = JsonConvert.DeserializeObject(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(message); await listGamesHandler.Handle(req, userName); break; } case ClientAction.CreateGame: { var req = JsonConvert.DeserializeObject(message); await createGameHandler.Handle(req, userName); break; } case ClientAction.JoinGame: { var req = JsonConvert.DeserializeObject(message); await joinGameHandler.Handle(req, userName); break; } case ClientAction.JoinByCode: { var req = JsonConvert.DeserializeObject(message); await joinByCodeHandler.Handle(req, userName); break; } case ClientAction.LoadGame: { var req = JsonConvert.DeserializeObject(message); await loadGameHandler.Handle(req, userName); break; } case ClientAction.Move: { var req = JsonConvert.DeserializeObject(message); await moveHandler.Handle(req, userName); break; } } } catch (OperationCanceledException ex) { logger.LogError(ex.Message); } catch (WebSocketException ex) { logger.LogInformation($"{nameof(WebSocketException)} in {nameof(SocketCommunicationManager)}."); logger.LogInformation("Probably tried writing to a closed socket."); logger.LogError(ex.Message); } } communicationManager.UnsubscribeFromBroadcastAndGames(userName); return; } } context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; return; } } }