mirror of
				https://github.com/jellyfin/jellyfin.git
				synced 2025-11-04 03:27:21 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			203 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			203 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
#pragma warning disable CS1591
 | 
						|
 | 
						|
using System;
 | 
						|
using System.Collections.Generic;
 | 
						|
using System.Globalization;
 | 
						|
using System.Threading.Tasks;
 | 
						|
using Emby.Server.Implementations.HttpServer;
 | 
						|
using MediaBrowser.Model.Services;
 | 
						|
using Microsoft.Extensions.Logging;
 | 
						|
 | 
						|
namespace Emby.Server.Implementations.Services
 | 
						|
{
 | 
						|
    public delegate object ActionInvokerFn(object intance, object request);
 | 
						|
 | 
						|
    public delegate void VoidActionInvokerFn(object intance, object request);
 | 
						|
 | 
						|
    public class ServiceController
 | 
						|
    {
 | 
						|
        private readonly ILogger<ServiceController> _logger;
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Initializes a new instance of the <see cref="ServiceController"/> class.
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="logger">The <see cref="ServiceController"/> logger.</param>
 | 
						|
        public ServiceController(ILogger<ServiceController> logger)
 | 
						|
        {
 | 
						|
            _logger = logger;
 | 
						|
        }
 | 
						|
 | 
						|
        public void Init(HttpListenerHost appHost, IEnumerable<Type> serviceTypes)
 | 
						|
        {
 | 
						|
            foreach (var serviceType in serviceTypes)
 | 
						|
            {
 | 
						|
                RegisterService(appHost, serviceType);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        public void RegisterService(HttpListenerHost appHost, Type serviceType)
 | 
						|
        {
 | 
						|
            // Make sure the provided type implements IService
 | 
						|
            if (!typeof(IService).IsAssignableFrom(serviceType))
 | 
						|
            {
 | 
						|
                _logger.LogWarning("Tried to register a service that does not implement IService: {ServiceType}", serviceType);
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            var processedReqs = new HashSet<Type>();
 | 
						|
 | 
						|
            var actions = ServiceExecGeneral.Reset(serviceType);
 | 
						|
 | 
						|
            foreach (var mi in serviceType.GetActions())
 | 
						|
            {
 | 
						|
                var requestType = mi.GetParameters()[0].ParameterType;
 | 
						|
                if (processedReqs.Contains(requestType))
 | 
						|
                {
 | 
						|
                    continue;
 | 
						|
                }
 | 
						|
 | 
						|
                processedReqs.Add(requestType);
 | 
						|
 | 
						|
                ServiceExecGeneral.CreateServiceRunnersFor(requestType, actions);
 | 
						|
 | 
						|
                // var returnMarker = GetTypeWithGenericTypeDefinitionOf(requestType, typeof(IReturn<>));
 | 
						|
                // var responseType = returnMarker != null ?
 | 
						|
                //      GetGenericArguments(returnMarker)[0]
 | 
						|
                //    : mi.ReturnType != typeof(object) && mi.ReturnType != typeof(void) ?
 | 
						|
                //      mi.ReturnType
 | 
						|
                //    : Type.GetType(requestType.FullName + "Response");
 | 
						|
 | 
						|
                RegisterRestPaths(appHost, requestType, serviceType);
 | 
						|
 | 
						|
                appHost.AddServiceInfo(serviceType, requestType);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        public readonly RestPath.RestPathMap RestPathMap = new RestPath.RestPathMap();
 | 
						|
 | 
						|
        public void RegisterRestPaths(HttpListenerHost appHost, Type requestType, Type serviceType)
 | 
						|
        {
 | 
						|
            var attrs = appHost.GetRouteAttributes(requestType);
 | 
						|
            foreach (var attr in attrs)
 | 
						|
            {
 | 
						|
                var restPath = new RestPath(appHost.CreateInstance, appHost.GetParseFn, requestType, serviceType, attr.Path, attr.Verbs, attr.IsHidden, attr.Summary, attr.Description);
 | 
						|
 | 
						|
                RegisterRestPath(restPath);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        private static readonly char[] InvalidRouteChars = new[] { '?', '&' };
 | 
						|
 | 
						|
        public void RegisterRestPath(RestPath restPath)
 | 
						|
        {
 | 
						|
            if (restPath.Path[0] != '/')
 | 
						|
            {
 | 
						|
                throw new ArgumentException(
 | 
						|
                    string.Format(
 | 
						|
                        CultureInfo.InvariantCulture,
 | 
						|
                        "Route '{0}' on '{1}' must start with a '/'",
 | 
						|
                        restPath.Path,
 | 
						|
                        restPath.RequestType.GetMethodName()));
 | 
						|
            }
 | 
						|
 | 
						|
            if (restPath.Path.IndexOfAny(InvalidRouteChars) != -1)
 | 
						|
            {
 | 
						|
                throw new ArgumentException(
 | 
						|
                    string.Format(
 | 
						|
                        CultureInfo.InvariantCulture,
 | 
						|
                        "Route '{0}' on '{1}' contains invalid chars. ",
 | 
						|
                        restPath.Path,
 | 
						|
                        restPath.RequestType.GetMethodName()));
 | 
						|
            }
 | 
						|
 | 
						|
            if (RestPathMap.TryGetValue(restPath.FirstMatchHashKey, out List<RestPath> pathsAtFirstMatch))
 | 
						|
            {
 | 
						|
                pathsAtFirstMatch.Add(restPath);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                RestPathMap[restPath.FirstMatchHashKey] = new List<RestPath>() { restPath };
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        public RestPath GetRestPathForRequest(string httpMethod, string pathInfo)
 | 
						|
        {
 | 
						|
            var matchUsingPathParts = RestPath.GetPathPartsForMatching(pathInfo);
 | 
						|
 | 
						|
            List<RestPath> firstMatches;
 | 
						|
 | 
						|
            var yieldedHashMatches = RestPath.GetFirstMatchHashKeys(matchUsingPathParts);
 | 
						|
            foreach (var potentialHashMatch in yieldedHashMatches)
 | 
						|
            {
 | 
						|
                if (!this.RestPathMap.TryGetValue(potentialHashMatch, out firstMatches))
 | 
						|
                {
 | 
						|
                    continue;
 | 
						|
                }
 | 
						|
 | 
						|
                var bestScore = -1;
 | 
						|
                RestPath bestMatch = null;
 | 
						|
                foreach (var restPath in firstMatches)
 | 
						|
                {
 | 
						|
                    var score = restPath.MatchScore(httpMethod, matchUsingPathParts);
 | 
						|
                    if (score > bestScore)
 | 
						|
                    {
 | 
						|
                        bestScore = score;
 | 
						|
                        bestMatch = restPath;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                if (bestScore > 0 && bestMatch != null)
 | 
						|
                {
 | 
						|
                    return bestMatch;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            var yieldedWildcardMatches = RestPath.GetFirstMatchWildCardHashKeys(matchUsingPathParts);
 | 
						|
            foreach (var potentialHashMatch in yieldedWildcardMatches)
 | 
						|
            {
 | 
						|
                if (!this.RestPathMap.TryGetValue(potentialHashMatch, out firstMatches))
 | 
						|
                {
 | 
						|
                    continue;
 | 
						|
                }
 | 
						|
 | 
						|
                var bestScore = -1;
 | 
						|
                RestPath bestMatch = null;
 | 
						|
                foreach (var restPath in firstMatches)
 | 
						|
                {
 | 
						|
                    var score = restPath.MatchScore(httpMethod, matchUsingPathParts);
 | 
						|
                    if (score > bestScore)
 | 
						|
                    {
 | 
						|
                        bestScore = score;
 | 
						|
                        bestMatch = restPath;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                if (bestScore > 0 && bestMatch != null)
 | 
						|
                {
 | 
						|
                    return bestMatch;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            return null;
 | 
						|
        }
 | 
						|
 | 
						|
        public Task<object> Execute(HttpListenerHost httpHost, object requestDto, IRequest req)
 | 
						|
        {
 | 
						|
            var requestType = requestDto.GetType();
 | 
						|
            req.OperationName = requestType.Name;
 | 
						|
 | 
						|
            var serviceType = httpHost.GetServiceTypeByRequest(requestType);
 | 
						|
 | 
						|
            var service = httpHost.CreateInstance(serviceType);
 | 
						|
 | 
						|
            if (service is IRequiresRequest serviceRequiresContext)
 | 
						|
            {
 | 
						|
                serviceRequiresContext.Request = req;
 | 
						|
            }
 | 
						|
 | 
						|
            // Executes the service and returns the result
 | 
						|
            return ServiceExecGeneral.Execute(serviceType, req, service, requestDto, requestType.GetMethodName());
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |