/// <summary> /// Initializes a new instance of the <see cref="T:LeanIPC.RemoteObject"/> class. /// </summary> /// <param name="handle">The handle to use.</param> public RemoteObject(RPCPeer peer, Type type, long handle) { m_peer = peer; m_handle = handle; m_remoteType = type; m_typelookups = m_remoteType.IsInterface ? new Type[] { m_remoteType }.Concat(m_remoteType.GetInterfaces()).Distinct().ToArray() : new Type[] { m_remoteType }; }
/// <summary> /// Creates a proxy instance that wraps a dictionary of properties /// </summary> /// <returns>The property decomposed instance.</returns> /// <param name="peer">The unused peer instance.</param> /// <param name="type">The remote type being wrapped.</param> /// <param name="interface">The interface presented as the wrapped.</param> /// <param name="values">The decomposed property values.</param> public static object WrapPropertyDecomposedInstance(this RPCPeer peer, Type type, Type @interface, object[] values) { var d = new Dictionary <string, object>(); var names = (string[])values[0]; for (var i = 0; i < names.Length; i++) { d[names[i]] = values[i + 1]; } var p = ProxyCreator.CreateAutomaticProxy <PropertyDecomposedObject>(peer, type, @interface, 0); var h = p.GetType().GetField("__remoteHandle", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(p) as PropertyDecomposedObject; h.m_values = d; return(p); }
/// <summary> /// Runs the RPC peer /// </summary> /// <returns>An awaitable task.</returns> public static Task RunClientRPCListenerAsync() { // Make sure we have loaded these assemblies into the current process var myTypes = new List <Type> { typeof(Ceen.AsyncLock), typeof(Ceen.Httpd.HttpServer), typeof(Ceen.Mvc.Controller) }; // Preload this, if possible try { myTypes.Add(Type.GetType("Ceen.Security.PRNG, Ceen.Security")); } catch { } // Read environment setup var path = Environment.GetEnvironmentVariable(SOCKET_PATH_VARIABLE_NAME); if (string.IsNullOrWhiteSpace(path)) { throw new ArgumentNullException($"No path found in the environment variable"); } var prefix = string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(SOCKET_PREFIX_VARIABLE_NAME)) ? string.Empty : "\0"; var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.IP); socket.Connect(new SockRock.UnixEndPoint(prefix + path)); var ipc = new LeanIPC.InterProcessConnection(new NetworkStream(socket, true)); var peer = new LeanIPC.RPCPeer(ipc, typeof(SpawnedServer)); // Pass back the SpawnedServer instance as a reference peer.TypeSerializer.RegisterSerializationAction(typeof(SpawnedServer), LeanIPC.SerializationAction.Reference); // Support these types with automatically generated proxies peer.AddAutomaticProxy(typeof(IStorageEntry), typeof(IStorageEntry)); peer.AddAutomaticProxy(typeof(IStorageCreator), typeof(IStorageCreator)); // Add support for EndPoint's peer.TypeSerializer.RegisterEndPointSerializers(); peer.TypeSerializer.RegisterIPEndPointSerializers(); return(Task.WhenAll( ipc.RunMainLoopAsync(true), ListenForRequests(peer, path, prefix) )); }
/// <summary> /// Initializes a new instance of the <see cref="T:LeanIPC.PropertyDecomposedObject"/> class. /// </summary> /// <param name="peer">The unused peer argument.</param> /// <param name="remotetype">The remote type.</param> /// <param name="handle">The unused handle argument.</param> public PropertyDecomposedObject(RPCPeer peer, Type remotetype, long handle) { m_peer = peer; m_remoteType = remotetype; }
/// <summary> /// Registers a custom serializer that decomposes an interface into the properties /// </summary> /// <param name="peer">The peer to register on.</param> /// <param name="filter">An optional filter for the properties</param> /// <typeparam name="T">The type to register the custom serializer for.</typeparam> public static RPCPeer RegisterPropertyDecomposer <T>(this RPCPeer peer, Func <System.Reflection.PropertyInfo, bool> filter = null) { RegisterPropertyDecomposer <T>(peer.TypeSerializer, filter); return(peer); }
/// <summary> /// Registers a custom serializer/deserializer for <see cref="IPEndPoint"/> /// </summary> /// <param name="peer">The peer to register on.</param> /// <returns>The peer instance</returns> public static RPCPeer RegisterIPEndPointSerializers(this RPCPeer peer) { RegisterIPEndPointSerializers(peer.TypeSerializer); return(peer); }
/// <summary> /// Creates a remote proxy for the given handle /// </summary> /// <returns>The create.</returns> /// <param name="peer">The peer to invoke the method on.</param> /// <param name="type">The remote type being wrapped.</param> /// <param name="interface">The interface to return</param> /// <param name="handle">The remote handle.</param> public static IRemoteInstance WrapRemote(this RPCPeer peer, Type type, Type @interface, long handle) { return((IRemoteInstance)ProxyCreator.CreateAutomaticProxy <RemoteObject>(peer, type, @interface, handle)); }
/// <summary> /// Creates a proxy class for the given interface that makes remote invocations /// </summary> /// <returns>The remote proxy.</returns> /// <param name="peer">The peer to invoke remote methods on.</param> /// <param name="type">The remote type to make the proxy for.</param> /// <param name="proxytype">The type to create a proxy with</param> /// <param name="handlertype">The type that the proxy is interfacing with, must implement IProxyHelper</param> /// <param name="handle">The remote handle.</param> public static object CreateRemoteProxy(RPCPeer peer, Type type, Type proxytype, Type handlertype, long handle) { if (type == null) { throw new ArgumentNullException(nameof(type)); } if (proxytype == null) { throw new ArgumentNullException(nameof(proxytype)); } if (handlertype == null) { throw new ArgumentNullException(nameof(handlertype)); } lock (_lock) { if (!_interfaceCache.ContainsKey(proxytype)) { // NOTE: Some of the code below actually supports a non-interface type, // but this only works if _all_ methods on the class are virtual // To avoid confusion, we only allow interfaces to be auto-proxied if (!proxytype.IsInterface) { throw new ArgumentException($"Cannot generate a proxy for the non-interface type {proxytype}"); } if (!handlertype.GetInterfaces().Any(x => x == typeof(IProxyHelper))) { throw new Exception($"The type {handlertype} does not implement {typeof(IProxyHelper)}"); } var typename = "DynamicProxy." + type.FullName + "." + Guid.NewGuid().ToString("N").Substring(0, 6); // Build an assembly and a module to contain the type var assemblyName = new AssemblyName($"{nameof(LeanIPC)}.{nameof(ProxyCreator)}.{typename}"); var assembly = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); var module = assembly.DefineDynamicModule(assemblyName.Name); // The RemoteObject constructor args var constructorArgs = new Type[] { typeof(RPCPeer), typeof(Type), typeof(long) }; var interfaces = new List <Type>(); if (proxytype.IsInterface) { interfaces.Add(proxytype); } if (!proxytype.GetInterfaces().Contains(typeof(IRemoteInstance)) && handlertype.GetInterfaces().Contains(typeof(IRemoteInstance))) { interfaces.Add(typeof(IRemoteInstance)); } if (!proxytype.GetInterfaces().Contains(typeof(IDisposable))) { interfaces.Add(typeof(IDisposable)); } var basetype = proxytype.IsInterface ? typeof(object) : proxytype; // Create the type definition var typeBuilder = module.DefineType(typename, TypeAttributes.Public, basetype, interfaces.ToArray()); // Create a field to store the remote object instance var remotefld = typeBuilder.DefineField("__remoteHandle", handlertype, FieldAttributes.Private | FieldAttributes.InitOnly); // Create the constructor that initializes the remote object field var constructor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, constructorArgs); var construtorIL = constructor.GetILGenerator(); var conm = handlertype.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, constructorArgs, null); if (conm == null) { throw new Exception($"The type {handlertype} does not have a constructor that takes the types ({typeof(RPCPeer)}, {typeof(Type)}, {typeof(long)})"); } construtorIL.Emit(OpCodes.Ldarg_0); construtorIL.Emit(OpCodes.Ldarg_1); construtorIL.Emit(OpCodes.Ldarg_2); construtorIL.Emit(OpCodes.Ldarg_3); construtorIL.Emit(OpCodes.Newobj, conm); construtorIL.Emit(OpCodes.Stfld, remotefld); construtorIL.Emit(OpCodes.Ret); // Explicitly wire the IRemoteInstance interface to call remotefld directly // Since we map this directly, we can treat property access as methods var explictMethods = ( interfaces.Contains(typeof(IRemoteInstance)) || proxytype.GetInterfaces().Contains(typeof(IRemoteInstance)) ? typeof(IRemoteInstance).GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance) : new MethodInfo[0] ) .Concat( typeof(IDisposable).GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance) ); foreach (var sourceMethod in explictMethods) { if (!proxytype.IsInterface) { continue; } var parameterTypes = sourceMethod.GetParameters().Select(x => x.ParameterType).ToArray(); // If the proxy type is IDisposable, we need to call the remote dispose before the local var isSpecialDispose = sourceMethod.DeclaringType == typeof(IDisposable) && proxytype.GetInterfaces().Contains(typeof(IDisposable)); // Replicate the source method var method = typeBuilder.DefineMethod( (isSpecialDispose ? string.Empty : sourceMethod.DeclaringType.Name + ".") + sourceMethod.Name, MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual, CallingConventions.Standard, sourceMethod.ReturnType, parameterTypes ); // Write the IL to call the method through the interface var methodIL = method.GetILGenerator(); // Invoke dispose on the remote instance first if (isSpecialDispose) { methodIL.Emit(OpCodes.Ldarg_0); methodIL.Emit(OpCodes.Ldfld, remotefld); methodIL.Emit(OpCodes.Ldstr, sourceMethod.Name); EmitTypeArray(methodIL, parameterTypes); EmitArgumentArray(methodIL, parameterTypes, 1); var methods = handlertype .GetMethods(BindingFlags.Public | BindingFlags.Instance) .Where(x => x.Name == nameof(IProxyHelper.HandleInvokeMethod)) .Where(x => !x.IsGenericMethod); methodIL.Emit(OpCodes.Callvirt, methods.First()); methodIL.Emit(OpCodes.Pop); } // Pass the call to the IProxyHelper instance methodIL.Emit(OpCodes.Ldarg_0); methodIL.Emit(OpCodes.Ldfld, remotefld); for (var i = 0; i < parameterTypes.Length; i++) { EmitLdarg(methodIL, i + 1); } methodIL.Emit(OpCodes.Callvirt, sourceMethod); methodIL.Emit(OpCodes.Ret); // Specify that our specially named method implements the interface method if (!isSpecialDispose) { typeBuilder.DefineMethodOverride(method, sourceMethod); } } // Add all methods foreach (var sourceMethod in AllImplementedMethods(proxytype, IGNORETYPES)) { if (sourceMethod.Name == "Dispose" && sourceMethod.GetParameters().Length == 0 && sourceMethod.ReturnType == typeof(void)) { continue; } if (!proxytype.IsInterface) { continue; } var parameterTypes = sourceMethod.GetParameters().Select(x => x.ParameterType).ToArray(); // Replicate the source method var method = typeBuilder.DefineMethod( sourceMethod.Name, MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Final | MethodAttributes.Virtual, CallingConventions.Standard, sourceMethod.ReturnType, parameterTypes ); var methodIL = method.GetILGenerator(); methodIL.Emit(OpCodes.Ldarg_0); methodIL.Emit(OpCodes.Ldfld, remotefld); methodIL.Emit(OpCodes.Ldstr, sourceMethod.Name); EmitTypeArray(methodIL, parameterTypes); EmitArgumentArray(methodIL, parameterTypes, 1); var isAsyncResult = sourceMethod.ReturnType == typeof(Task) || (sourceMethod.ReturnType.IsConstructedGenericType && sourceMethod.ReturnType.GetGenericTypeDefinition() == typeof(Task <>)); var methods = handlertype .GetMethods(BindingFlags.Public | BindingFlags.Instance) .Where(x => x.Name == (isAsyncResult ? nameof(IProxyHelper.HandleInvokeMethodAsync) : nameof(IProxyHelper.HandleInvokeMethod))); if (sourceMethod.ReturnType == typeof(void)) { // We discard the null object from the method methodIL.Emit(OpCodes.Callvirt, methods.First(x => !x.IsGenericMethodDefinition)); methodIL.Emit(OpCodes.Pop); } else if (sourceMethod.ReturnType == typeof(Task)) { // We return the Task from the underlying call methodIL.Emit(OpCodes.Callvirt, methods.First(x => !x.IsGenericMethodDefinition)); } else { // Unwrap the Task<T>, so the generic argument is T // or just use the return type for the method var genericParameter = sourceMethod.ReturnType.IsConstructedGenericType && sourceMethod.ReturnType.GetGenericTypeDefinition() == typeof(Task <>) ? sourceMethod.ReturnType.GetGenericArguments().First() : sourceMethod.ReturnType; methodIL.Emit(OpCodes.Callvirt, methods.First(x => x.IsGenericMethodDefinition).MakeGenericMethod(genericParameter)); } methodIL.Emit(OpCodes.Ret); } // Add all properties foreach (var sourceProperty in AllImplementedProperties(proxytype, IGNORETYPES)) { if (!proxytype.IsInterface) { continue; } var indexParameters = sourceProperty.GetIndexParameters().Select(x => x.ParameterType).ToArray(); var property = typeBuilder.DefineProperty( sourceProperty.Name, PropertyAttributes.None, sourceProperty.PropertyType, indexParameters ); if (sourceProperty.CanRead) { var getMethod = typeBuilder.DefineMethod( "get_" + property.Name, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.NewSlot, CallingConventions.HasThis, sourceProperty.PropertyType, indexParameters ); var getMethodIL = getMethod.GetILGenerator(); getMethodIL.Emit(OpCodes.Ldarg_0); getMethodIL.Emit(OpCodes.Ldfld, remotefld); getMethodIL.Emit(OpCodes.Ldstr, sourceProperty.Name); EmitTypeArray(getMethodIL, indexParameters); EmitArgumentArray(getMethodIL, indexParameters, 1); var isAsyncResult = sourceProperty.PropertyType.IsConstructedGenericType && sourceProperty.PropertyType.GetGenericTypeDefinition() == typeof(Task <>); var baseGetMethod = handlertype .GetMethods(BindingFlags.Public | BindingFlags.Instance) .Where(x => x.Name == (isAsyncResult ? nameof(IProxyHelper.HandleInvokePropertyGetAsync) : nameof(IProxyHelper.HandleInvokePropertyGet))) .Where(x => x.IsGenericMethodDefinition) .First(); var genericParameter = isAsyncResult ? sourceProperty.PropertyType.GetGenericArguments().First() : sourceProperty.PropertyType; getMethodIL.Emit(OpCodes.Callvirt, baseGetMethod.MakeGenericMethod(genericParameter)); getMethodIL.Emit(OpCodes.Ret); property.SetGetMethod(getMethod); } if (sourceProperty.CanWrite) { var setMethod = typeBuilder.DefineMethod( "set_" + property.Name, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.NewSlot, CallingConventions.HasThis, typeof(void), new Type[] { sourceProperty.PropertyType }.Concat(indexParameters).ToArray() ); var setMethodIL = setMethod.GetILGenerator(); setMethodIL.Emit(OpCodes.Ldarg_0); setMethodIL.Emit(OpCodes.Ldfld, remotefld); setMethodIL.Emit(OpCodes.Ldstr, sourceProperty.Name); setMethodIL.Emit(OpCodes.Ldarg_1); if (sourceProperty.PropertyType.IsValueType) { setMethodIL.Emit(OpCodes.Box, sourceProperty.PropertyType); } EmitTypeArray(setMethodIL, indexParameters); EmitArgumentArray(setMethodIL, indexParameters, 2); setMethodIL.Emit(OpCodes.Callvirt, handlertype.GetMethod(nameof(IProxyHelper.HandleInvokePropertySet), BindingFlags.Public | BindingFlags.Instance)); setMethodIL.Emit(OpCodes.Ret); property.SetSetMethod(setMethod); } } _interfaceCache[proxytype] = typeBuilder.CreateTypeInfo(); } } // Return the instance //var typeInfo = typeBuilder.CreateTypeInfo(); //var instanceConstructor = typeInfo.GetConstructor(constructorArgs); return(Activator.CreateInstance(_interfaceCache[proxytype], new object[] { peer, type, handle })); }
/// <summary> /// Creates a proxy class for the given interface that makes remote invocations /// </summary> /// <returns>The remote proxy.</returns> /// <param name="peer">The peer to invoke remote methods on.</param> /// <param name="type">The remote type to make the proxy for.</param> /// <param name="proxytype">The type to create a proxy with</param> /// <param name="handle">The remote handle.</param> /// <typeparam name="TBase">The type that the proxy is interfacing with</typeparam> public static object CreateAutomaticProxy <TBase>(RPCPeer peer, Type type, Type proxytype, long handle) where TBase : IProxyHelper { return(CreateRemoteProxy(peer, type, proxytype, typeof(TBase), handle)); }