@ -21,8 +21,6 @@ namespace Emby.Server.Implementations.Services
readonly bool [ ] componentsWithSeparators ;
private readonly string restPath ;
private readonly string allowedVerbs ;
private readonly bool allowsAllVerbs ;
public bool IsWildCardPath { get ; private set ; }
private readonly string [ ] literalsToMatch ;
@ -46,15 +44,7 @@ namespace Emby.Server.Implementations.Services
/// </summary>
public int TotalComponentsCount { get ; set ; }
public string [ ] Verbs
{
get
{
return allowsAllVerbs
? new [ ] { "ANY" }
: AllowedVerbs . Split ( new [ ] { ',' , ' ' } , StringSplitOptions . RemoveEmptyEntries ) ;
}
}
public string [ ] Verbs { get ; private set ; }
public Type RequestType { get ; private set ; }
@ -62,19 +52,11 @@ namespace Emby.Server.Implementations.Services
public string Summary { get ; private set ; }
public string Notes { get ; private set ; }
public bool AllowsAllVerbs { get { return this . allowsAllVerbs ; } }
public string AllowedVerbs { get { return this . allowedVerbs ; } }
public int Priority { get ; set ; } //passed back to RouteAttribute
public static string [ ] GetPathPartsForMatching ( string pathInfo )
{
var parts = pathInfo . ToLower ( ) . Split ( PathSeperatorChar )
. Where ( x = > ! String . IsNullOrEmpty ( x ) ) . ToArray ( ) ;
return parts ;
return pathInfo . ToLower ( ) . Split ( new [ ] { PathSeperatorChar } , StringSplitOptions . RemoveEmptyEntries ) ;
}
public static List < string > GetFirstMatchHashKeys ( string [ ] pathPartsForMatching )
@ -109,18 +91,13 @@ namespace Emby.Server.Implementations.Services
return list ;
}
public RestPath ( Func < Type , object > createInstanceFn , Func < Type , Func < string , object > > getParseFn , Type requestType , string path , string verbs , string summary = null , string notes = null )
public RestPath ( Func < Type , object > createInstanceFn , Func < Type , Func < string , object > > getParseFn , Type requestType , string path , string verbs , string summary = null )
{
this . RequestType = requestType ;
this . Summary = summary ;
this . Notes = notes ;
this . restPath = path ;
this . allowsAllVerbs = verbs = = null | | String . Equals ( verbs , WildCard , StringComparison . OrdinalIgnoreCase ) ;
if ( ! this . allowsAllVerbs )
{
this . allowedVerbs = verbs . ToUpper ( ) ;
}
this . Verbs = string . IsNullOrWhiteSpace ( verbs ) ? ServiceExecExtensions . AllVerbs : verbs . ToUpper ( ) . Split ( new [ ] { ' ' , ',' } , StringSplitOptions . RemoveEmptyEntries ) ;
var componentsList = new List < string > ( ) ;
@ -153,7 +130,6 @@ namespace Emby.Server.Implementations.Services
this . PathComponentsCount = this . componentsWithSeparators . Length ;
string firstLiteralMatch = null ;
var sbHashKey = new StringBuilder ( ) ;
for ( var i = 0 ; i < components . Length ; i + + )
{
var component = components [ i ] ;
@ -172,7 +148,6 @@ namespace Emby.Server.Implementations.Services
else
{
this . literalsToMatch [ i ] = component . ToLower ( ) ;
sbHashKey . Append ( i + PathSeperatorChar . ToString ( ) + this . literalsToMatch ) ;
if ( firstLiteralMatch = = null )
{
@ -198,9 +173,6 @@ namespace Emby.Server.Implementations.Services
? this . PathComponentsCount + PathSeperator + firstLiteralMatch
: WildCardChar + PathSeperator + firstLiteralMatch ;
this . IsValid = sbHashKey . Length > 0 ;
this . UniqueMatchHashKey = sbHashKey . ToString ( ) ;
this . typeDeserializer = new StringMapTypeDeserializer ( createInstanceFn , getParseFn , this . RequestType ) ;
RegisterCaseInsenstivePropertyNameMappings ( ) ;
}
@ -220,26 +192,46 @@ namespace Emby.Server.Implementations.Services
} ;
private static List< Type> _ excludeTypes = new List < Type > { typeof( Stream ) } ;
private static Type excludeType = typeof( Stream ) ;
internal static PropertyInfo[ ] GetSerializableProperties ( Type type )
internal static List< PropertyInfo > GetSerializableProperties ( Type type )
{
var properties = GetPublicProperties ( type ) ;
var readableProperties = properties . Where ( x = > x . GetMethod ! = null ) ;
var list = new List < PropertyInfo > ( ) ;
var props = GetPublicProperties ( type ) ;
// else return those properties that are not decorated with IgnoreDataMember
return readableProperties
. Where ( prop = > prop . GetCustomAttributes ( true )
. All ( attr = >
foreach ( var prop in props )
{
if ( prop . GetMethod = = null )
{
continue ;
}
if ( excludeType = = prop . PropertyType )
{
continue ;
}
var ignored = false ;
foreach ( var attr in prop . GetCustomAttributes ( true ) )
{
if ( IgnoreAttributesNamed . Contains ( attr . GetType ( ) . Name ) )
{
var name = attr . GetType ( ) . Name ;
return ! IgnoreAttributesNamed . Contains ( name ) ;
} ) )
. Where ( prop = > ! _excludeTypes . Contains ( prop . PropertyType ) )
. ToArray ( ) ;
ignored = true ;
break ;
}
}
if ( ! ignored )
{
list . Add ( prop ) ;
}
}
// else return those properties that are not decorated with IgnoreDataMember
return list ;
}
private static PropertyInfo [ ] GetPublicProperties ( Type type )
private static List< PropertyInfo > GetPublicProperties ( Type type )
{
if ( type . GetTypeInfo ( ) . IsInterface )
{
@ -269,12 +261,19 @@ namespace Emby.Server.Implementations.Services
propertyInfos . InsertRange ( 0 , newPropertyInfos ) ;
}
return propertyInfos .ToArray ( propertyInfos . Count ) ;
return propertyInfos ;
}
return GetTypesPublicProperties ( type )
. Where ( t = > t . GetIndexParameters ( ) . Length = = 0 ) // ignore indexed properties
. ToArray ( ) ;
var list = new List < PropertyInfo > ( ) ;
foreach ( var t in GetTypesPublicProperties ( type ) )
{
if ( t . GetIndexParameters ( ) . Length = = 0 )
{
list . Add ( t ) ;
}
}
return list ;
}
private static PropertyInfo [ ] GetTypesPublicProperties ( Type subType )
@ -289,16 +288,11 @@ namespace Emby.Server.Implementations.Services
return pis . ToArray ( pis . Count ) ;
}
public bool IsValid { get ; set ; }
/// <summary>
/// Provide for quick lookups based on hashes that can be determined from a request url
/// </summary>
public string FirstMatchHashKey { get ; private set ; }
public string UniqueMatchHashKey { get ; private set ; }
private readonly StringMapTypeDeserializer typeDeserializer ;
private readonly Dictionary < string , string > propertyNamesMap = new Dictionary < string , string > ( ) ;
@ -321,8 +315,14 @@ namespace Emby.Server.Implementations.Services
score + = Math . Max ( ( 10 - VariableArgsCount ) , 1 ) * 100 ;
//Exact verb match is better than ANY
var exactVerb = String . Equals ( httpMethod , AllowedVerbs , StringComparison . OrdinalIgnoreCase ) ;
score + = exactVerb ? 10 : 1 ;
if ( Verbs . Length = = 1 & & string . Equals ( httpMethod , Verbs [ 0 ] , StringComparison . OrdinalIgnoreCase ) )
{
score + = 10 ;
}
else
{
score + = 1 ;
}
return score ;
}
@ -346,7 +346,7 @@ namespace Emby.Server.Implementations.Services
return false ;
}
if ( ! this . allowsAllVerbs & & ! StringContains ( this . allowedVerbs , httpMethod ) )
if ( ! Verbs . Contains ( httpMethod , StringComparer . OrdinalIgnoreCase ) )
{
//logger.Info("allowsAllVerbs mismatch for {0} for {1} allowedverbs {2}", httpMethod, string.Join("/", withPathInfoParts), this.allowedVerbs);
return false ;
@ -457,8 +457,7 @@ namespace Emby.Server.Implementations.Services
public object CreateRequest ( string pathInfo , Dictionary < string , string > queryStringAndFormData , object fromInstance )
{
var requestComponents = pathInfo . Split ( PathSeperatorChar )
. Where ( x = > ! String . IsNullOrEmpty ( x ) ) . ToArray ( ) ;
var requestComponents = pathInfo . Split ( new [ ] { PathSeperatorChar } , StringSplitOptions . RemoveEmptyEntries ) ;
ExplodeComponents ( ref requestComponents ) ;
@ -555,10 +554,5 @@ namespace Emby.Server.Implementations.Services
return this . typeDeserializer . PopulateFromMap ( fromInstance , requestKeyValuesMap ) ;
}
public override int GetHashCode ( )
{
return UniqueMatchHashKey . GetHashCode ( ) ;
}
}
}