Пример #1
0
 /// <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 };
 }
Пример #2
0
        /// <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);
        }
Пример #3
0
        /// <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)
                       ));
        }
Пример #4
0
 /// <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;
 }
Пример #5
0
 /// <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);
 }
Пример #6
0
 /// <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);
 }
Пример #7
0
 /// <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));
 }
Пример #8
0
        /// <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 }));
        }
Пример #9
0
 /// <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));
 }