besterd/source/handlers/handler.d

227 lines
5.9 KiB
D

module handlers.handler;
import std.stdio : writeln;
import std.socket : Socket, AddressFamily, parseAddress, SocketType, SocketOSException, UnixAddress;
import std.json : JSONValue, JSONType;
import utils.debugging : debugPrint;
import handlers.response;
import base.net;
import connection.message;
import server.server : BesterServer;
import std.process : spawnProcess, Pid;
public final class MessageHandler
{
/* The path to the message handler executable */
private string executablePath;
/* The UNIX domain socket */
private Socket domainSocket;
/* The pluginName/type */
private string pluginName;
/* The UNIX domain socket path */
public string socketPath;
/* The BesterServer being used */
public BesterServer server;
/* The PID of the process */
private Pid pid;
public Socket getSocket()
{
return domainSocket;
}
this(BesterServer server, string executablePath, string socketPath, string pluginName)
{
/* Set the plugin name */
this.pluginName = pluginName;
/* Set the socket path */
this.socketPath = socketPath;
/* Set the server this handler is associated with */
this.server = server;
/* Start the message handler */
startHandlerExecutable();
}
private void startHandlerExecutable()
{
// pid = spawnProcess(executablePath);
}
public string getPluginName()
{
return pluginName;
}
public Socket getNewSocket()
{
/* Create the UNIX domain socket */
Socket domainSocket = new Socket(AddressFamily.UNIX, SocketType.STREAM);
/* Connect to the socket at the socket path */
domainSocket.connect(new UnixAddress(socketPath));
return domainSocket;
}
private void initializeUNIXSocket(string socketPath)
{
/* Create the UNIX domain socket */
domainSocket = new Socket(AddressFamily.UNIX, SocketType.STREAM);
/* Bind it to the socket path */
domainSocket.connect(new UnixAddress(socketPath));
}
private static string[] getAvailableTypes(JSONValue handlerBlock)
{
/* Available types as strings */
string[] availableTypesStrings;
/* Get the available handlers */
JSONValue availableTypes;
/* TODO: Bounds check */
availableTypes = handlerBlock["availableTypes"];
/* Make sure it is an array */
if(availableTypes.type == JSONType.array)
{
/* Get the array of available types */
JSONValue[] availableTypesArray = availableTypes.array;
for(uint i = 0; i < availableTypesArray.length; i++)
{
/* Make sure that it is a string */
if(availableTypesArray[i].type == JSONType.string)
{
/* Add the type handler to the list of available types */
availableTypesStrings ~= availableTypesArray[i].str;
debugPrint("Module wanted: " ~ availableTypesArray[i].str);
}
else
{
/* TODO: Error handling here */
debugPrint("Available type not of type JSON string");
}
}
}
else
{
/* TODO: Error handling */
}
return availableTypesStrings;
}
private static string[2] getConfigurationArray(string pluginName, JSONValue typeMapBlock)
{
/* The configuration string */
string[2] configurationString;
/* The module block */
JSONValue moduleBlock;
/* TODO: Bounds check */
moduleBlock = typeMapBlock[pluginName];
/* Module block mst be of tpe JSON object */
if(moduleBlock.type == JSONType.object)
{
/* TODO: Set the executable path */
configurationString[0] = moduleBlock["handlerBinary"].str;
/* TODO: Set the UNIX domain socket path */
configurationString[1] = moduleBlock["unixDomainSocketPath"].str;
}
else
{
/* TODO: Error handling */
}
return configurationString;
}
/* TODO: Implement me */
public static MessageHandler[] constructHandlers(BesterServer server, JSONValue handlerBlock)
{
/* List of loaded message handlers */
MessageHandler[] handlers;
/* TODO: Throwing error from inside this function */
string[] availableTypes = getAvailableTypes(handlerBlock);
for(uint i = 0; i < availableTypes.length; i++)
{
/* Load module */
string pluginName = availableTypes[i];
debugPrint("Loading module \"" ~ pluginName ~ "\"...");
try
{
JSONValue typeMap;
/* TODO: Bounds check */
typeMap = handlerBlock["typeMap"];
string[2] configuration = getConfigurationArray(pluginName, typeMap);
debugPrint("Module executable at: \"" ~ configuration[0] ~ "\"");
debugPrint("Module socket path at: \"" ~ configuration[1] ~ "\"");
MessageHandler constructedMessageHandler = new MessageHandler(server, configuration[0], configuration[1], pluginName);
handlers ~= constructedMessageHandler;
debugPrint("Module \"" ~ pluginName ~ "\" loaded");
}
catch(SocketOSException exception)
{
debugPrint("Error whilst loading module \"" ~ pluginName ~ "\": " ~ exception.toString());
}
}
return handlers;
}
/**
* Sends the payload to the designated message handler and gets
* the response message from the handler and returns it.
*/
public HandlerResponse handleMessage(JSONValue payload)
{
/* TODO: If unix sock is down, this just hangs, we should see if the socket file exists first */
/* Handler's UNIX domain socket */
Socket handlerSocket = getNewSocket();
/* Send the payload to the message handler */
debugPrint("Sending payload over to handler for \"" ~ getPluginName() ~ "\".");
sendMessage(handlerSocket, payload);
/* Get the payload sent from the message handler in response */
debugPrint("Waiting for response from handler for \"" ~ getPluginName() ~ "\".");
JSONValue response;
try
{
receiveMessage(handlerSocket, response);
}
catch(NetworkException exception)
{
/* TODO: Implement error handling here and send a repsonse back (Issue: https://github.com/besterprotocol/besterd/issues/10) */
debugPrint("Error communicating with message handler");
}
finally
{
/* Always close the socket */
handlerSocket.close();
debugPrint("Closed UNIX domain socket to handler");
}
return new HandlerResponse(server, this, response);
}
}