mirror of
https://github.com/Stone-Red-Code/RemoteExec.git
synced 2026-09-04 09:06:20 +02:00
Add infrastructure to support multiple execution environments
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
using RemoteExec.Server.Utilities;
|
||||
using RemoteExec.Shared;
|
||||
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace RemoteExec.Server.Services;
|
||||
|
||||
public class AssemblyLoadContextExecutionEnvironment : ExecutionEnvironment
|
||||
{
|
||||
public override string Name => "AssemblyLoadContext";
|
||||
|
||||
private RemoteJobAssemblyLoadContext? assemblyLoadContext;
|
||||
|
||||
public override async Task<RemoteExecutionResult> ExecuteTaskAsync(RemoteExecutionRequest request)
|
||||
{
|
||||
if (assemblyLoadContext is null)
|
||||
{
|
||||
throw new InvalidOperationException("The execution environment has not been prepared.");
|
||||
}
|
||||
|
||||
// Check if assembly is already loaded in the context
|
||||
Assembly? assembly = assemblyLoadContext.Assemblies.FirstOrDefault(a => a.GetName().FullName == request.AssemblyName);
|
||||
|
||||
// If not loaded, request and load it into the context
|
||||
assembly ??= assemblyLoadContext.LoadFromBytes(await RequestAssemblyAsync(request.AssemblyName));
|
||||
|
||||
Type type = assembly.GetType(request.TypeName, throwOnError: true)!;
|
||||
|
||||
Type[] argTypes = request.ArgumentTypes
|
||||
.Select(Type.GetType)
|
||||
.ToArray()!;
|
||||
|
||||
MethodInfo? method = type.GetMethod(
|
||||
request.MethodName,
|
||||
BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic,
|
||||
binder: null,
|
||||
argTypes,
|
||||
modifiers: null) ?? throw new MissingMethodException(request.TypeName, request.MethodName);
|
||||
|
||||
// Pre-load all referenced assemblies to avoid triggering Resolving event during Invoke
|
||||
await AssemblyUtilities.PreLoadReferencedAssembliesAsync(assemblyLoadContext, assembly, RequestAssemblyAsync);
|
||||
|
||||
ParameterInfo[] parameters = method.GetParameters();
|
||||
|
||||
if (parameters.Length != request.Arguments.Length)
|
||||
{
|
||||
throw new ArgumentException($"Argument count mismatch: expected {parameters.Length}, received {request.Arguments.Length}");
|
||||
}
|
||||
|
||||
object?[] invokeArgs = new object?[request.Arguments.Length];
|
||||
|
||||
for (int i = 0; i < invokeArgs.Length; i++)
|
||||
{
|
||||
Type targetType = parameters[i].ParameterType;
|
||||
object arg = request.Arguments[i];
|
||||
|
||||
if (arg is JsonElement je)
|
||||
{
|
||||
// Deserialize the JSON element into the expected CLR type
|
||||
invokeArgs[i] = JsonSerializer.Deserialize(je.GetRawText(), targetType);
|
||||
}
|
||||
else if (arg == null)
|
||||
{
|
||||
invokeArgs[i] = null;
|
||||
}
|
||||
else if (!targetType.IsInstanceOfType(arg))
|
||||
{
|
||||
// Fallback for simple primitive conversions
|
||||
invokeArgs[i] = Convert.ChangeType(arg, targetType);
|
||||
}
|
||||
else
|
||||
{
|
||||
invokeArgs[i] = arg;
|
||||
}
|
||||
}
|
||||
|
||||
object? result = method.Invoke(null, invokeArgs);
|
||||
|
||||
if (result is Task taskResult)
|
||||
{
|
||||
await taskResult.ConfigureAwait(false);
|
||||
Type returnType = method.ReturnType;
|
||||
if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>))
|
||||
{
|
||||
PropertyInfo resultProperty = returnType.GetProperty("Result")!;
|
||||
result = resultProperty.GetValue(taskResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For non-generic Task, result is null
|
||||
result = null;
|
||||
}
|
||||
}
|
||||
|
||||
return new RemoteExecutionResult
|
||||
{
|
||||
Result = result
|
||||
};
|
||||
}
|
||||
|
||||
public override Task PrepareEnvironmentAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
assemblyLoadContext = new RemoteJobAssemblyLoadContext($"RemoteJob_{Guid.NewGuid()}");
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public override Task CleanupEnvironmentAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
assemblyLoadContext?.Unload();
|
||||
assemblyLoadContext = null;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user