2020-05-29 05:28:19 -04:00
#pragma warning disable CS1591
2016-11-11 14:55:12 -05: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-12 20:07:48 -05:00
namespace Emby.Server.Implementations.Services
2016-11-11 14:55:12 -05:00
{
public static class ServiceExecExtensions
{
2017-08-30 23:49:38 -04:00
public static string [ ] AllVerbs = new [ ] {
2017-02-12 20:07:48 -05: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-30 23:49:38 -04:00
} ;
2017-08-30 14:52:29 -04:00
public static List < MethodInfo > GetActions ( this Type serviceType )
2016-11-11 14:55:12 -05:00
{
2017-08-30 14:52:29 -04:00
var list = new List < MethodInfo > ( ) ;
foreach ( var mi in serviceType . GetRuntimeMethods ( ) )
2016-11-11 14:55:12 -05:00
{
2017-08-30 14:52:29 -04:00
if ( ! mi . IsPublic )
{
continue ;
}
if ( mi . IsStatic )
{
continue ;
}
2016-11-11 14:55:12 -05:00
if ( mi . GetParameters ( ) . Length ! = 1 )
continue ;
2017-02-12 20:07:48 -05:00
var actionName = mi . Name ;
2017-08-30 14:52:29 -04:00
if ( ! AllVerbs . Contains ( actionName , StringComparer . OrdinalIgnoreCase ) )
2016-11-11 14:55:12 -05:00
continue ;
2017-08-30 14:52:29 -04:00
list . Add ( mi ) ;
2016-11-11 14:55:12 -05:00
}
2017-08-30 14:52:29 -04:00
return list ;
2016-11-11 14:55:12 -05:00
}
}
internal static class ServiceExecGeneral
{
2017-09-01 15:24:39 -04:00
private static Dictionary < string , ServiceMethod > execMap = new Dictionary < string , ServiceMethod > ( ) ;
2016-11-11 14:55:12 -05:00
2017-02-12 20:07:48 -05:00
public static void CreateServiceRunnersFor ( Type requestType , List < ServiceMethod > actions )
2016-11-11 14:55:12 -05:00
{
foreach ( var actionCtx in actions )
{
2020-06-20 04:35:29 -04:00
if ( execMap . ContainsKey ( actionCtx . Id ) )
{
continue ;
}
2016-11-11 14:55:12 -05:00
execMap [ actionCtx . Id ] = actionCtx ;
}
}
2018-09-12 13:26:21 -04:00
public static Task < object > Execute ( Type serviceType , IRequest request , object instance , object requestDto , string requestName )
2016-11-11 14:55:12 -05:00
{
2017-02-12 20:07:48 -05:00
var actionName = request . Verb ? ? "POST" ;
2016-11-11 14:55:12 -05:00
2019-01-17 12:47:41 -05:00
if ( execMap . TryGetValue ( ServiceMethod . Key ( serviceType , actionName , requestName ) , out ServiceMethod actionContext ) )
2016-11-11 14:55:12 -05:00
{
if ( actionContext . RequestFilters ! = null )
{
foreach ( var requestFilter in actionContext . RequestFilters )
{
requestFilter . RequestFilter ( request , request . Response , requestDto ) ;
2019-07-28 17:53:19 -04:00
if ( request . Response . HasStarted )
2018-09-12 13:26:21 -04:00
{
Task . FromResult < object > ( null ) ;
}
2016-11-11 14:55:12 -05:00
}
}
var response = actionContext . ServiceAction ( instance , requestDto ) ;
2019-09-02 02:19:29 -04:00
if ( response is Task taskResponse )
2016-11-11 14:55:12 -05:00
{
2018-09-12 13:26:21 -04:00
return GetTaskResult ( taskResponse ) ;
2016-11-11 14:55:12 -05:00
}
2018-09-12 13:26:21 -04:00
return Task . FromResult ( response ) ;
2016-11-11 14:55:12 -05:00
}
2019-01-27 06:03:43 -05:00
var expectedMethodName = actionName . Substring ( 0 , 1 ) + actionName . Substring ( 1 ) . ToLowerInvariant ( ) ;
2017-02-13 15:54:28 -05: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 14:55:12 -05:00
}
2018-09-12 13:26:21 -04:00
private static async Task < object > GetTaskResult ( Task task )
{
try
{
2019-09-02 02:19:29 -04:00
if ( task is Task < object > taskObject )
2018-09-12 13:26:21 -04:00
{
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 )
{
2019-09-02 02:19:29 -04:00
return null ; // return null for void Task's
2018-09-12 13:26:21 -04:00
}
}
2017-02-12 20:07:48 -05:00
public static List < ServiceMethod > Reset ( Type serviceType )
2016-11-11 14:55:12 -05:00
{
2017-02-12 20:07:48 -05:00
var actions = new List < ServiceMethod > ( ) ;
2016-11-11 14:55:12 -05:00
foreach ( var mi in serviceType . GetActions ( ) )
{
2017-02-12 20:07:48 -05:00
var actionName = mi . Name ;
2016-11-11 14:55:12 -05:00
var args = mi . GetParameters ( ) ;
var requestType = args [ 0 ] . ParameterType ;
2017-02-12 20:07:48 -05:00
var actionCtx = new ServiceMethod
2016-11-11 14:55:12 -05:00
{
2017-02-13 15:54:28 -05:00
Id = ServiceMethod . Key ( serviceType , actionName , requestType . GetMethodName ( ) )
2016-11-11 14:55:12 -05:00
} ;
2019-09-02 02:19:29 -04:00
actionCtx . ServiceAction = CreateExecFn ( serviceType , requestType , mi ) ;
2016-11-11 14:55:12 -05:00
var reqFilters = new List < IHasRequestFilter > ( ) ;
foreach ( var attr in mi . GetCustomAttributes ( true ) )
{
2019-09-02 02:19:29 -04:00
if ( attr is IHasRequestFilter hasReqFilter )
{
2016-11-11 14:55:12 -05:00
reqFilters . Add ( hasReqFilter ) ;
2019-09-02 02:19:29 -04:00
}
2016-11-11 14:55:12 -05:00
}
if ( reqFilters . Count > 0 )
2019-09-02 02:19:29 -04:00
{
2018-12-28 10:48:26 -05:00
actionCtx . RequestFilters = reqFilters . OrderBy ( i = > i . Priority ) . ToArray ( ) ;
2019-09-02 02:19:29 -04:00
}
2016-11-11 14:55:12 -05: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 ) )
{
2019-09-02 02:19:29 -04:00
var executeFunc = Expression . Lambda < ActionInvokerFn > (
callExecute ,
serviceParam ,
requestDtoParam ) . Compile ( ) ;
2016-11-11 14:55:12 -05:00
return executeFunc ;
}
else
{
2019-09-02 02:19:29 -04:00
var executeFunc = Expression . Lambda < VoidActionInvokerFn > (
callExecute ,
serviceParam ,
requestDtoParam ) . Compile ( ) ;
2016-11-11 14:55:12 -05:00
return ( service , request ) = >
{
executeFunc ( service , request ) ;
return null ;
} ;
}
}
}
2018-12-28 10:48:26 -05:00
}