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 }