jellyfin-server/Emby.Server.Implementations/Services/ServiceExec.cs

223 lines
7.6 KiB
C#
Raw Normal View History

2016-11-11 19:55:12 +00:00
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;
using MediaBrowser.Model.Services;
2017-02-13 01:07:48 +00:00
namespace Emby.Server.Implementations.Services
2016-11-11 19:55:12 +00:00
{
public static class ServiceExecExtensions
{
2017-08-31 03:49:38 +00:00
public static string[] AllVerbs = new[] {
2017-02-13 01:07:48 +00:00
"OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT", // RFC 2616
"PROPFIND", "PROPPATCH", "MKCOL", "COPY", "MOVE", "LOCK", "UNLOCK", // RFC 2518
"VERSION-CONTROL", "REPORT", "CHECKOUT", "CHECKIN", "UNCHECKOUT",
"MKWORKSPACE", "UPDATE", "LABEL", "MERGE", "BASELINE-CONTROL", "MKACTIVITY", // RFC 3253
"ORDERPATCH", // RFC 3648
"ACL", // RFC 3744
"PATCH", // https://datatracker.ietf.org/doc/draft-dusseault-http-patch/
"SEARCH", // https://datatracker.ietf.org/doc/draft-reschke-webdav-search/
"BCOPY", "BDELETE", "BMOVE", "BPROPFIND", "BPROPPATCH", "NOTIFY",
"POLL", "SUBSCRIBE", "UNSUBSCRIBE"
2017-08-31 03:49:38 +00:00
};
public static HashSet<string> AllVerbsSet = new HashSet<string>(AllVerbs);
2017-02-13 01:07:48 +00:00
2017-08-30 18:52:29 +00:00
public static List<MethodInfo> GetActions(this Type serviceType)
2016-11-11 19:55:12 +00:00
{
2017-08-30 18:52:29 +00:00
var list = new List<MethodInfo>();
foreach (var mi in serviceType.GetRuntimeMethods())
2016-11-11 19:55:12 +00:00
{
2017-08-30 18:52:29 +00:00
if (!mi.IsPublic)
{
continue;
}
if (mi.IsStatic)
{
continue;
}
2016-11-11 19:55:12 +00:00
if (mi.GetParameters().Length != 1)
continue;
2017-02-13 01:07:48 +00:00
var actionName = mi.Name;
2017-08-30 18:52:29 +00:00
if (!AllVerbs.Contains(actionName, StringComparer.OrdinalIgnoreCase))
2016-11-11 19:55:12 +00:00
continue;
2017-08-30 18:52:29 +00:00
list.Add(mi);
2016-11-11 19:55:12 +00:00
}
2017-08-30 18:52:29 +00:00
return list;
2016-11-11 19:55:12 +00:00
}
}
internal static class ServiceExecGeneral
{
2017-09-01 19:24:39 +00:00
private static Dictionary<string, ServiceMethod> execMap = new Dictionary<string, ServiceMethod>();
2016-11-11 19:55:12 +00:00
2017-02-13 01:07:48 +00:00
public static void CreateServiceRunnersFor(Type requestType, List<ServiceMethod> actions)
2016-11-11 19:55:12 +00:00
{
foreach (var actionCtx in actions)
{
if (execMap.ContainsKey(actionCtx.Id)) continue;
execMap[actionCtx.Id] = actionCtx;
}
}
2018-09-12 17:26:21 +00:00
public static Task<object> Execute(Type serviceType, IRequest request, object instance, object requestDto, string requestName)
2016-11-11 19:55:12 +00:00
{
2017-02-13 01:07:48 +00:00
var actionName = request.Verb ?? "POST";
2016-11-11 19:55:12 +00:00
2017-02-13 01:07:48 +00:00
ServiceMethod actionContext;
2017-08-30 18:52:29 +00:00
if (ServiceExecGeneral.execMap.TryGetValue(ServiceMethod.Key(serviceType, actionName, requestName), out actionContext))
2016-11-11 19:55:12 +00:00
{
if (actionContext.RequestFilters != null)
{
foreach (var requestFilter in actionContext.RequestFilters)
{
requestFilter.RequestFilter(request, request.Response, requestDto);
2018-09-12 17:26:21 +00:00
if (request.Response.IsClosed)
{
Task.FromResult<object>(null);
}
2016-11-11 19:55:12 +00:00
}
}
var response = actionContext.ServiceAction(instance, requestDto);
var taskResponse = response as Task;
if (taskResponse != null)
{
2018-09-12 17:26:21 +00:00
return GetTaskResult(taskResponse);
2016-11-11 19:55:12 +00:00
}
2018-09-12 17:26:21 +00:00
return Task.FromResult(response);
2016-11-11 19:55:12 +00:00
}
var expectedMethodName = actionName.Substring(0, 1) + actionName.Substring(1).ToLower();
2017-02-13 20:54:28 +00:00
throw new NotImplementedException(string.Format("Could not find method named {1}({0}) or Any({0}) on Service {2}", requestDto.GetType().GetMethodName(), expectedMethodName, serviceType.GetMethodName()));
2016-11-11 19:55:12 +00:00
}
2018-09-12 17:26:21 +00:00
private static async Task<object> GetTaskResult(Task task)
{
try
{
var taskObject = task as Task<object>;
if (taskObject != null)
{
return await taskObject.ConfigureAwait(false);
}
await task.ConfigureAwait(false);
var type = task.GetType().GetTypeInfo();
if (!type.IsGenericType)
{
return null;
}
var resultProperty = type.GetDeclaredProperty("Result");
if (resultProperty == null)
{
return null;
}
var result = resultProperty.GetValue(task);
// hack alert
if (result.GetType().Name.IndexOf("voidtaskresult", StringComparison.OrdinalIgnoreCase) != -1)
{
return null;
}
return result;
}
catch (TypeAccessException)
{
return null; //return null for void Task's
}
}
2017-02-13 01:07:48 +00:00
public static List<ServiceMethod> Reset(Type serviceType)
2016-11-11 19:55:12 +00:00
{
2017-02-13 01:07:48 +00:00
var actions = new List<ServiceMethod>();
2016-11-11 19:55:12 +00:00
foreach (var mi in serviceType.GetActions())
{
2017-02-13 01:07:48 +00:00
var actionName = mi.Name;
2016-11-11 19:55:12 +00:00
var args = mi.GetParameters();
var requestType = args[0].ParameterType;
2017-02-13 01:07:48 +00:00
var actionCtx = new ServiceMethod
2016-11-11 19:55:12 +00:00
{
2017-02-13 20:54:28 +00:00
Id = ServiceMethod.Key(serviceType, actionName, requestType.GetMethodName())
2016-11-11 19:55:12 +00:00
};
try
{
actionCtx.ServiceAction = CreateExecFn(serviceType, requestType, mi);
}
catch
{
//Potential problems with MONO, using reflection for fallback
actionCtx.ServiceAction = (service, request) =>
mi.Invoke(service, new[] { request });
}
var reqFilters = new List<IHasRequestFilter>();
foreach (var attr in mi.GetCustomAttributes(true))
{
var hasReqFilter = attr as IHasRequestFilter;
if (hasReqFilter != null)
reqFilters.Add(hasReqFilter);
}
if (reqFilters.Count > 0)
2018-12-28 15:48:26 +00:00
actionCtx.RequestFilters = reqFilters.OrderBy(i => i.Priority).ToArray();
2016-11-11 19:55:12 +00:00
actions.Add(actionCtx);
}
return actions;
}
private static ActionInvokerFn CreateExecFn(Type serviceType, Type requestType, MethodInfo mi)
{
var serviceParam = Expression.Parameter(typeof(object), "serviceObj");
var serviceStrong = Expression.Convert(serviceParam, serviceType);
var requestDtoParam = Expression.Parameter(typeof(object), "requestDto");
var requestDtoStrong = Expression.Convert(requestDtoParam, requestType);
Expression callExecute = Expression.Call(
serviceStrong, mi, requestDtoStrong);
if (mi.ReturnType != typeof(void))
{
var executeFunc = Expression.Lambda<ActionInvokerFn>
(callExecute, serviceParam, requestDtoParam).Compile();
return executeFunc;
}
else
{
var executeFunc = Expression.Lambda<VoidActionInvokerFn>
(callExecute, serviceParam, requestDtoParam).Compile();
return (service, request) =>
{
executeFunc(service, request);
return null;
};
}
}
}
2018-12-28 15:48:26 +00:00
}