You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
203 lines
7.1 KiB
203 lines
7.1 KiB
#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());
|
|
}
|
|
}
|
|
}
|