1 module handlers.handler; 2 3 import std.stdio : writeln; 4 import std.socket : Socket, AddressFamily, parseAddress, SocketType, SocketOSException, UnixAddress; 5 import std.json : JSONValue, JSONType; 6 import utils.debugging : debugPrint; 7 import handlers.response; 8 import base.net; 9 import utils.message : sendMessage, receiveMessage; 10 import server.server : BesterServer; 11 import std.process : spawnProcess, Pid; 12 13 public final class MessageHandler 14 { 15 /* The path to the message handler executable */ 16 private string executablePath; 17 18 /* The UNIX domain socket */ 19 private Socket domainSocket; 20 21 /* The pluginName/type */ 22 private string pluginName; 23 24 /* The UNIX domain socket path */ 25 public string socketPath; 26 27 /* The BesterServer being used */ 28 public BesterServer server; 29 30 /* The PID of the process */ 31 private Pid pid; 32 33 public Socket getSocket() 34 { 35 return domainSocket; 36 } 37 38 this(BesterServer server, string executablePath, string socketPath, string pluginName) 39 { 40 /* Set the plugin name */ 41 this.pluginName = pluginName; 42 43 /* Set the socket path */ 44 this.socketPath = socketPath; 45 46 /* Set the server this handler is associated with */ 47 this.server = server; 48 49 /* Start the message handler */ 50 startHandlerExecutable(); 51 } 52 53 private void startHandlerExecutable() 54 { 55 // pid = spawnProcess(executablePath); 56 } 57 58 public string getPluginName() 59 { 60 return pluginName; 61 } 62 63 public Socket getNewSocket() 64 { 65 /* Create the UNIX domain socket */ 66 Socket domainSocket = new Socket(AddressFamily.UNIX, SocketType.STREAM); 67 68 /* Connect to the socket at the socket path */ 69 domainSocket.connect(new UnixAddress(socketPath)); 70 71 return domainSocket; 72 } 73 74 private static string[] getAvailableTypes(JSONValue handlerBlock) 75 { 76 /* Available types as strings */ 77 string[] availableTypesStrings; 78 79 /* Get the available handlers */ 80 JSONValue availableTypes; 81 82 /* TODO: Bounds check */ 83 availableTypes = handlerBlock["availableTypes"]; 84 85 /* Make sure it is an array */ 86 if(availableTypes.type == JSONType.array) 87 { 88 /* Get the array of available types */ 89 JSONValue[] availableTypesArray = availableTypes.array; 90 91 for(uint i = 0; i < availableTypesArray.length; i++) 92 { 93 /* Make sure that it is a string */ 94 if(availableTypesArray[i].type == JSONType..string) 95 { 96 /* Add the type handler to the list of available types */ 97 availableTypesStrings ~= availableTypesArray[i].str; 98 debugPrint("Module wanted: " ~ availableTypesArray[i].str); 99 } 100 else 101 { 102 /* TODO: Error handling here */ 103 debugPrint("Available type not of type JSON string"); 104 } 105 } 106 } 107 else 108 { 109 /* TODO: Error handling */ 110 } 111 112 return availableTypesStrings; 113 } 114 115 private static string[2] getConfigurationArray(string pluginName, JSONValue typeMapBlock) 116 { 117 /* The configuration string */ 118 string[2] configurationString; 119 120 /* The module block */ 121 JSONValue moduleBlock; 122 123 /* TODO: Bounds check */ 124 moduleBlock = typeMapBlock[pluginName]; 125 126 /* Module block mst be of tpe JSON object */ 127 if(moduleBlock.type == JSONType.object) 128 { 129 /* TODO: Set the executable path */ 130 configurationString[0] = moduleBlock["handlerBinary"].str; 131 132 /* TODO: Set the UNIX domain socket path */ 133 configurationString[1] = moduleBlock["unixDomainSocketPath"].str; 134 } 135 else 136 { 137 /* TODO: Error handling */ 138 } 139 140 return configurationString; 141 } 142 143 /* TODO: Implement me */ 144 public static MessageHandler[] constructHandlers(BesterServer server, JSONValue handlerBlock) 145 { 146 /* List of loaded message handlers */ 147 MessageHandler[] handlers; 148 149 /* TODO: Throwing error from inside this function */ 150 string[] availableTypes = getAvailableTypes(handlerBlock); 151 152 for(uint i = 0; i < availableTypes.length; i++) 153 { 154 /* Load module */ 155 string pluginName = availableTypes[i]; 156 debugPrint("Loading module \"" ~ pluginName ~ "\"..."); 157 158 try 159 { 160 JSONValue typeMap; 161 162 /* TODO: Bounds check */ 163 typeMap = handlerBlock["typeMap"]; 164 165 string[2] configuration = getConfigurationArray(pluginName, typeMap); 166 debugPrint("Module executable at: \"" ~ configuration[0] ~ "\""); 167 debugPrint("Module socket path at: \"" ~ configuration[1] ~ "\""); 168 MessageHandler constructedMessageHandler = new MessageHandler(server, configuration[0], configuration[1], pluginName); 169 handlers ~= constructedMessageHandler; 170 debugPrint("Module \"" ~ pluginName ~ "\" loaded"); 171 } 172 catch(SocketOSException exception) 173 { 174 debugPrint("Error whilst loading module \"" ~ pluginName ~ "\": " ~ exception.toString()); 175 } 176 } 177 178 return handlers; 179 } 180 181 /** 182 * Sends the payload to the designated message handler and gets 183 * the response message from the handler and returns it. 184 */ 185 public HandlerResponse handleMessage(JSONValue payload) 186 { 187 /* TODO: If unix sock is down, this just hangs, we should see if the socket file exists first */ 188 /* Handler's UNIX domain socket */ 189 Socket handlerSocket = getNewSocket(); 190 191 /* Send the payload to the message handler */ 192 debugPrint("Sending payload over to handler for \"" ~ getPluginName() ~ "\"."); 193 sendMessage(handlerSocket, payload); 194 195 /* Get the payload sent from the message handler in response */ 196 debugPrint("Waiting for response from handler for \"" ~ getPluginName() ~ "\"."); 197 JSONValue response; 198 199 try 200 { 201 receiveMessage(handlerSocket, response); 202 } 203 catch(NetworkException exception) 204 { 205 /* TODO: Implement error handling here and send a repsonse back (Issue: https://github.com/besterprotocol/besterd/issues/10) */ 206 debugPrint("Error communicating with message handler"); 207 } 208 finally 209 { 210 /* Always close the socket */ 211 handlerSocket.close(); 212 debugPrint("Closed UNIX domain socket to handler"); 213 } 214 215 return new HandlerResponse(server, this, response); 216 } 217 }