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
} ;
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
2019-01-17 17:47:41 +00:00
if ( execMap . TryGetValue ( ServiceMethod . Key ( serviceType , actionName , requestName ) , out ServiceMethod 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 ) ;
2019-03-07 21:26:23 +00:00
if ( request . Response . OriginalResponse . HasStarted )
2018-09-12 17:26:21 +00:00
{
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
}
2019-01-27 11:03:43 +00:00
var expectedMethodName = actionName . Substring ( 0 , 1 ) + actionName . Substring ( 1 ) . ToLowerInvariant ( ) ;
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
}