besterd/source/handlers/response.d

354 lines
10 KiB
D
Raw Normal View History

module handlers.response;
2020-06-16 18:19:46 +02:00
import std.json : JSONValue, JSONException, parseJSON, toJSON;
import std.conv : to;
import utils.debugging : debugPrint;
import std.string : cmp;
import std.stdio : writeln;
import connection.connection;
2020-04-26 22:16:07 +02:00
import base.types;
import std.socket : Socket, SocketOSException, AddressFamily, SocketType, ProtocolType, parseAddress;
2020-05-01 09:27:17 +02:00
import utils.message : receiveMessage, sendMessage;
2020-04-28 22:41:14 +02:00
import handlers.handler;
2020-04-29 15:40:55 +02:00
import std.string : split;
2020-04-30 13:30:03 +02:00
import server.server : BesterServer;
2020-05-01 17:48:45 +02:00
import handlers.commands : Command;
/**
* The type of the command the message handler wants
* us to run
*/
2020-05-01 17:48:45 +02:00
private enum CommandType : ubyte
2020-04-24 18:55:19 +02:00
{
/* Simple message flow (always end point) */
SEND_CLIENTS, SEND_SERVERS, SEND_HANDLER,
2020-05-01 17:48:45 +02:00
/* Unknown command */
UNKNOWN
2020-04-24 18:55:19 +02:00
}
2020-05-05 09:25:05 +02:00
/**
* Represents a response message from a handler.
*/
public final class HandlerResponse
{
/**
* The message-handler's response.
*/
private JSONValue messageResponse;
2020-04-24 18:55:19 +02:00
/* The command to be executed */
private CommandType commandType;
2020-04-28 22:41:14 +02:00
/* The handler that caused such a response to be illicited */
private MessageHandler handler;
2020-04-30 18:03:50 +02:00
/* The associated server */
2020-04-30 15:55:40 +02:00
private BesterServer server;
2020-04-30 13:30:03 +02:00
/**
* Constructs a new `HandlerResponse` object that represents the
* message handler's response message and the execution of it.
*/
2020-04-30 15:55:40 +02:00
this(BesterServer server, MessageHandler handler, JSONValue messageResponse)
{
/* Set the message-handler's response message */
this.messageResponse = messageResponse;
2020-04-28 22:41:14 +02:00
/* Set the handler who caused this reponse to occur */
this.handler = handler;
2020-04-30 18:03:50 +02:00
/* Set the server associated with this message handler */
this.server = server;
2020-04-30 14:25:12 +02:00
/* Attempt parsing the message and error checking it */
parse(messageResponse);
}
private void parse(JSONValue handlerResponse)
{
/**
* Handles the response sent back to the server from the
* message handler.
*/
/* Get the status */
ulong statusCode;
/* Error? */
bool error;
2020-04-27 13:00:16 +02:00
/* TODO: Bounds checking, type checking */
try
{
/* Get the header block */
JSONValue headerBlock = handlerResponse["header"];
2020-04-27 13:00:16 +02:00
/* Get the status */
statusCode = to!(ulong)(headerBlock["status"].str());
debugPrint("Status code: " ~ to!(string)(statusCode));
2020-04-27 13:00:16 +02:00
/* If the status is 0, then it is all fine */
if(statusCode == 0)
{
debugPrint("Status is fine, the handler ran correctly");
2020-04-27 13:00:16 +02:00
/* The command block */
JSONValue commandBlock = headerBlock["command"];
2020-04-27 13:00:16 +02:00
/**
* Get the command that the message handler wants the
* server to run.
*/
string serverCommand = commandBlock["type"].str;
debugPrint("Handler->Server command: \"" ~ serverCommand ~ "\"");
2020-04-27 13:00:16 +02:00
/* Check the command to be run */
if(cmp(serverCommand, "sendClients") == 0)
{
/* Set the command type to SEND_CLIENTS */
commandType = CommandType.SEND_CLIENTS;
/* TODO: Error check and do accesses JSON that would be done in `.execute` */
}
2020-04-27 13:00:16 +02:00
else if(cmp(serverCommand, "sendServers") == 0)
{
2020-04-27 13:00:16 +02:00
/* Set the command type to SEND_SERVERS */
commandType = CommandType.SEND_SERVERS;
/* TODO: Error check and do accesses JSON that would be done in `.execute` */
}
2020-04-27 13:00:16 +02:00
else if(cmp(serverCommand, "sendHandler") == 0)
{
/* Set the command type to SEND_HAANDLER */
commandType = CommandType.SEND_HANDLER;
/* TODO: Error check and do accesses JSON that would be done in `.execute` */
}
else
{
/* TODO: Error handling */
debugPrint("The message handler is using an invalid command");
}
}
else
{
/* If the message handler returned a response in error */
debugPrint("Message handler returned an error code: " ~ to!(string)(statusCode));
error = true;
}
}
catch(JSONException exception)
{
debugPrint("<<< There was an error handling the response message >>>\n\n" ~ exception.toString());
error = true;
}
/**
* If an error was envountered anyway down the processing of the
* message-handler then raise a `ResponseError` exception.
*/
if(error)
{
throw new ResponseError(messageResponse, statusCode);
}
}
2020-05-05 09:25:05 +02:00
/**
* Executes the command. Either `sendClients`, `sendServers`
* or `sendHandler`.
*/
2020-05-14 11:22:41 +02:00
public void execute(BesterConnection originalRequester, string messageID)
{
2020-04-24 18:55:19 +02:00
/* If the command is SEND_CLIENTS */
2020-04-24 19:10:57 +02:00
if(commandType == CommandType.SEND_CLIENTS)
2020-04-24 18:55:19 +02:00
{
/* Get the list of clients to send to */
string[] clients;
2020-04-24 19:12:26 +02:00
JSONValue[] clientList = messageResponse["header"]["command"]["data"].array();
2020-04-24 18:55:19 +02:00
for(ulong i = 0; i < clientList.length; i++)
{
clients ~= clientList[i].str();
}
debugPrint("Users wanting to send to: " ~ to!(string)(clients));
/* Find the users that are wanting to be sent to */
BesterConnection[] connectionList = originalRequester.server.getClients(clients);
2020-04-28 22:41:14 +02:00
//debugPrint("Users matched online on server: " ~ to!(string)(connectionList));
2020-04-28 22:41:14 +02:00
/* The fully response message to send back */
2020-04-28 18:19:27 +02:00
JSONValue clientPayload;
2020-04-28 22:41:14 +02:00
/* Set the header of the response */
2020-05-15 15:52:33 +02:00
JSONValue headerBlock;
headerBlock["messageType"] = "receivedMessage";
2020-04-29 18:49:39 +02:00
clientPayload["header"] = headerBlock;
2020-04-28 22:41:14 +02:00
/* Set the payload of the response */
JSONValue payloadBlock;
payloadBlock["data"] = messageResponse["data"];
payloadBlock["type"] = handler.getPluginName();
clientPayload["payload"] = payloadBlock;
2020-04-30 13:30:03 +02:00
/**
2020-05-14 11:12:17 +02:00
* Loop through each BesterConnection in connectionList and
* send the message-handler payload response message to each
* of them.
*/
bool allSuccess = true;
for(ulong i = 0; i < connectionList.length; i++)
{
/* Get the conneciton */
BesterConnection clientConnection = connectionList[i];
try
{
/* Get the client's socket */
Socket clientSocket = clientConnection.getSocket();
//debugPrint("IsAlive?: " ~ to!(string)(clientSocket.isAlive()));
/* Send the message to the client */
debugPrint("Sending handler's response to client \"" ~ clientConnection.toString() ~ "\"...");
sendMessage(clientSocket, clientPayload);
debugPrint("Sending handler's response to client \"" ~ clientConnection.toString() ~ "\"... [sent]");
}
catch(SocketOSException exception)
{
/**
2020-05-14 11:12:17 +02:00
* If there was an error sending to the client, this can happen
* if the client has disconnected but hasn't yet been removed from
* the connections array and hence we try to send on a dead socket
* or get the remoteAddress on a dead socket, which causes a
* SocketOSException to be called.
*/
debugPrint("Attempted interacting with dead socket");
2020-05-14 14:03:54 +02:00
allSuccess = false;
}
}
debugPrint("SEND_CLIENTS: Completed run");
/**
* Send a status report here.
*/
2020-05-14 11:22:41 +02:00
originalRequester.sendStatusReport(cast(BesterConnection.StatusType)!allSuccess, messageID);
2020-04-24 18:55:19 +02:00
}
2020-04-24 19:10:57 +02:00
else if (commandType == CommandType.SEND_SERVERS)
{
/* Get the list of servers to send to */
string[] servers;
2020-04-24 19:12:26 +02:00
JSONValue[] serverList = messageResponse["header"]["command"]["data"].array();
2020-04-24 19:10:57 +02:00
for(ulong i = 0; i < serverList.length; i++)
{
servers ~= serverList[i].str();
}
/* TODO: Implement me */
writeln("Servers wanting to send to ", servers);
2020-04-30 13:30:03 +02:00
2020-04-29 18:49:39 +02:00
/* The fully response message to send back */
JSONValue serverPayload;
/* Set the header of the response */
JSONValue headerBlock;
headerBlock["scope"] = "server";
serverPayload["header"] = headerBlock;
/* Set the payload of the response */
JSONValue payloadBlock;
payloadBlock["data"] = messageResponse["data"];
payloadBlock["type"] = handler.getPluginName();
serverPayload["payload"] = payloadBlock;
/* Attempt connecting to each server and sending the payload */
2020-05-14 11:12:58 +02:00
bool allSuccess = true;
for(ulong i = 0; i < servers.length; i++)
{
/* Get the current server address and port */
2020-04-29 15:40:55 +02:00
string serverString = servers[i];
string host = serverString.split(":")[0];
ushort port = to!(ushort)(serverString.split(":")[1]);
try
{
Socket serverConnection = new Socket(AddressFamily.INET, SocketType.STREAM, ProtocolType.TCP);
2020-04-30 13:30:03 +02:00
/* Connect to the server */
debugPrint("Connecting to server \"" ~ serverConnection.toString() ~ "\"...");
2020-04-29 15:40:55 +02:00
serverConnection.connect(parseAddress(host, port));
2020-04-30 13:30:03 +02:00
debugPrint("Connecting to server \"" ~ serverConnection.toString() ~ "\"... [connected]");
2020-04-30 13:30:03 +02:00
/* Send the payload */
debugPrint("Sending handler's response to server \"" ~ serverConnection.toString() ~ "\"...");
sendMessage(serverConnection, serverPayload);
2020-04-30 13:30:03 +02:00
debugPrint("Sending handler's response to server \"" ~ serverConnection.toString() ~ "\"... [sent]");
/* Close the connection to the server */
serverConnection.close();
debugPrint("Closed connection to server \"" ~ serverConnection.toString() ~ "\"");
}
catch(Exception e)
{
/* TODO: Be more specific with the above exception type */
debugPrint("Error whilst sending payload to server: " ~ e.toString());
2020-05-14 14:03:54 +02:00
allSuccess = false;
}
}
2020-05-01 12:19:21 +02:00
debugPrint("SEND_SERVERS: Completed run");
2020-05-14 11:12:58 +02:00
/**
* Send a status report here.
*/
2020-05-14 11:22:41 +02:00
originalRequester.sendStatusReport(cast(BesterConnection.StatusType)!allSuccess, messageID);
2020-04-24 19:10:57 +02:00
}
else if (commandType == CommandType.SEND_HANDLER)
{
2020-04-25 18:58:32 +02:00
/* Name of the handler to send the message to */
2020-04-30 14:25:12 +02:00
string handler = messageResponse["header"]["command"]["data"].str();
2020-04-25 18:58:32 +02:00
debugPrint("Handler to forward to: " ~ handler);
/* Lookup the payloadType handler */
MessageHandler chosenHandler = server.findHandler(handler);
2020-05-01 22:23:24 +02:00
/* Send the data to the message handler */
HandlerResponse handlerResponse = chosenHandler.handleMessage(messageResponse["data"]);
2020-05-01 22:23:24 +02:00
/* Execute the code (this here, recursive) */
2020-05-14 11:22:41 +02:00
handlerResponse.execute(originalRequester, messageID);
2020-05-01 12:19:21 +02:00
debugPrint("SEND_HANDLER: Completed run");
}
2020-05-01 22:24:01 +02:00
else
{
2020-05-01 22:24:01 +02:00
/* TODO: */
}
}
override public string toString()
{
return messageResponse.toPrettyString();
}
}
/**
* Represents an error in handling the response.
*/
2020-04-26 22:16:07 +02:00
public final class ResponseError : BesterException
{
2020-04-27 13:00:16 +02:00
/* */
/* The status code that resulted in the response handling error */
private ulong statusCode;
this(JSONValue messageResponse, ulong statusCode)
{
/* TODO: Set message afterwards again */
super("");
}
}