/// <summary> /// Static constructor /// /// Initialise the available RPC methods by inspecting the class /// </summary> static RpcConnection() { // Find all methods on the instance decorated with the RpcCall attribute foreach (MethodInfo method in typeof(RpcConnection).GetRuntimeMethods()) { // Is the method marked for RPC export ? RpcExport exported = null; foreach (object attribute in method.GetCustomAttributes(true)) { if (attribute is RpcExport) { exported = (RpcExport)attribute; break; } } if (exported == null) { continue; } // Build up information about the call RpcMethod methodInfo = new RpcMethod(); methodInfo.Method = method; methodInfo.Exported = exported; ParameterInfo[] args = method.GetParameters(); methodInfo.Arguments = new string[args.Length]; for (int i = 0; i < args.Length; i++) { methodInfo.Arguments[i] = args[i].Name; } // Add it to the cache (TODO: Should check for duplicates) s_functionCache.Add(methodInfo.Exported.FunctionName, methodInfo); } }
/// <summary> /// Static constructor /// /// Initialise the available RPC methods by inspecting the class /// </summary> static RpcConnection() { // Find all methods on the instance decorated with the RpcCall attribute foreach (MethodInfo method in typeof(RpcConnection).GetRuntimeMethods()) { // Is the method marked for RPC export ? RpcExport exported = null; foreach (object attribute in method.GetCustomAttributes(true)) if (attribute is RpcExport) { exported = (RpcExport)attribute; break; } if (exported == null) continue; // Build up information about the call RpcMethod methodInfo = new RpcMethod(); methodInfo.Method = method; methodInfo.Exported = exported; ParameterInfo[] args = method.GetParameters(); methodInfo.Arguments = new string[args.Length]; for (int i = 0; i < args.Length; i++) methodInfo.Arguments[i] = args[i].Name; // Add it to the cache (TODO: Should check for duplicates) s_functionCache.Add(methodInfo.Exported.FunctionName, methodInfo); } }
/// <summary> /// Process an RPC call /// /// This implementation is very manual but is probably enough for the very /// small set of API calls we support. /// </summary> /// <param name="data"></param> private void ProcessRpcCall(IDictionary <string, object> data) { Dictionary <string, object> result = new Dictionary <string, object>(); result[FunctionSequence] = data[FunctionSequence]; try { // Is it a valid function name ? if (!s_functionCache.ContainsKey(data[FunctionName].ToString())) { throw new RpcException(String.Format("Unsupported or unrecognised function '{0}'", data[FunctionName])); } // Verify the arguments are at least a dictionary IDictionary <string, object> args = data[FunctionParameters] as IDictionary <string, object>; if (args == null) { throw new RpcException("Incorrectly formed RPC request"); } // Are all the required arguments present ? RpcMethod method = s_functionCache[data[FunctionName].ToString()]; if (!ContainsKeys(args, method.Arguments)) { throw new RpcException("One or more required arguments are missing."); } if (method.Arguments.Length != args.Count) { throw new RpcException("Unexpected additional arguments were provided."); } // Is authentication required ? if (method.Exported.AuthenticationRequired && !m_authenticated) { throw new RpcException("Authentication required"); } // Map named parameters to an array for invokation object[] callParams = new object[method.Arguments.Length]; for (int i = 0; i < method.Arguments.Length; i++) { callParams[i] = args[method.Arguments[i]]; } // Finally, call the method result[CallResult] = method.Method.Invoke(this, callParams); result[CallStatus] = true; } catch (Exception ex) { result[CallStatus] = false; result[CallResult] = ex.Message; } // Finally we can send back the response string json = JsonParser.ToJson(result); lock (m_socket) { m_socket.Send(json); } }