ServiceController.cs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. #pragma warning disable CS1591
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Globalization;
  5. using System.Threading.Tasks;
  6. using Emby.Server.Implementations.HttpServer;
  7. using MediaBrowser.Model.Services;
  8. using Microsoft.Extensions.Logging;
  9. namespace Emby.Server.Implementations.Services
  10. {
  11. public delegate object ActionInvokerFn(object intance, object request);
  12. public delegate void VoidActionInvokerFn(object intance, object request);
  13. public class ServiceController
  14. {
  15. private readonly ILogger<ServiceController> _logger;
  16. /// <summary>
  17. /// Initializes a new instance of the <see cref="ServiceController"/> class.
  18. /// </summary>
  19. /// <param name="logger">The <see cref="ServiceController"/> logger.</param>
  20. public ServiceController(ILogger<ServiceController> logger)
  21. {
  22. _logger = logger;
  23. }
  24. public void Init(HttpListenerHost appHost, IEnumerable<Type> serviceTypes)
  25. {
  26. foreach (var serviceType in serviceTypes)
  27. {
  28. RegisterService(appHost, serviceType);
  29. }
  30. }
  31. public void RegisterService(HttpListenerHost appHost, Type serviceType)
  32. {
  33. // Make sure the provided type implements IService
  34. if (!typeof(IService).IsAssignableFrom(serviceType))
  35. {
  36. _logger.LogWarning("Tried to register a service that does not implement IService: {ServiceType}", serviceType);
  37. return;
  38. }
  39. var processedReqs = new HashSet<Type>();
  40. var actions = ServiceExecGeneral.Reset(serviceType);
  41. foreach (var mi in serviceType.GetActions())
  42. {
  43. var requestType = mi.GetParameters()[0].ParameterType;
  44. if (processedReqs.Contains(requestType))
  45. {
  46. continue;
  47. }
  48. processedReqs.Add(requestType);
  49. ServiceExecGeneral.CreateServiceRunnersFor(requestType, actions);
  50. // var returnMarker = GetTypeWithGenericTypeDefinitionOf(requestType, typeof(IReturn<>));
  51. // var responseType = returnMarker != null ?
  52. // GetGenericArguments(returnMarker)[0]
  53. // : mi.ReturnType != typeof(object) && mi.ReturnType != typeof(void) ?
  54. // mi.ReturnType
  55. // : Type.GetType(requestType.FullName + "Response");
  56. RegisterRestPaths(appHost, requestType, serviceType);
  57. appHost.AddServiceInfo(serviceType, requestType);
  58. }
  59. }
  60. public readonly RestPath.RestPathMap RestPathMap = new RestPath.RestPathMap();
  61. public void RegisterRestPaths(HttpListenerHost appHost, Type requestType, Type serviceType)
  62. {
  63. var attrs = appHost.GetRouteAttributes(requestType);
  64. foreach (var attr in attrs)
  65. {
  66. var restPath = new RestPath(appHost.CreateInstance, appHost.GetParseFn, requestType, serviceType, attr.Path, attr.Verbs, attr.IsHidden, attr.Summary, attr.Description);
  67. RegisterRestPath(restPath);
  68. }
  69. }
  70. private static readonly char[] InvalidRouteChars = new[] { '?', '&' };
  71. public void RegisterRestPath(RestPath restPath)
  72. {
  73. if (restPath.Path[0] != '/')
  74. {
  75. throw new ArgumentException(
  76. string.Format(
  77. CultureInfo.InvariantCulture,
  78. "Route '{0}' on '{1}' must start with a '/'",
  79. restPath.Path,
  80. restPath.RequestType.GetMethodName()));
  81. }
  82. if (restPath.Path.IndexOfAny(InvalidRouteChars) != -1)
  83. {
  84. throw new ArgumentException(
  85. string.Format(
  86. CultureInfo.InvariantCulture,
  87. "Route '{0}' on '{1}' contains invalid chars. ",
  88. restPath.Path,
  89. restPath.RequestType.GetMethodName()));
  90. }
  91. if (RestPathMap.TryGetValue(restPath.FirstMatchHashKey, out List<RestPath> pathsAtFirstMatch))
  92. {
  93. pathsAtFirstMatch.Add(restPath);
  94. }
  95. else
  96. {
  97. RestPathMap[restPath.FirstMatchHashKey] = new List<RestPath>() { restPath };
  98. }
  99. }
  100. public RestPath GetRestPathForRequest(string httpMethod, string pathInfo)
  101. {
  102. var matchUsingPathParts = RestPath.GetPathPartsForMatching(pathInfo);
  103. List<RestPath> firstMatches;
  104. var yieldedHashMatches = RestPath.GetFirstMatchHashKeys(matchUsingPathParts);
  105. foreach (var potentialHashMatch in yieldedHashMatches)
  106. {
  107. if (!this.RestPathMap.TryGetValue(potentialHashMatch, out firstMatches))
  108. {
  109. continue;
  110. }
  111. var bestScore = -1;
  112. RestPath bestMatch = null;
  113. foreach (var restPath in firstMatches)
  114. {
  115. var score = restPath.MatchScore(httpMethod, matchUsingPathParts);
  116. if (score > bestScore)
  117. {
  118. bestScore = score;
  119. bestMatch = restPath;
  120. }
  121. }
  122. if (bestScore > 0 && bestMatch != null)
  123. {
  124. return bestMatch;
  125. }
  126. }
  127. var yieldedWildcardMatches = RestPath.GetFirstMatchWildCardHashKeys(matchUsingPathParts);
  128. foreach (var potentialHashMatch in yieldedWildcardMatches)
  129. {
  130. if (!this.RestPathMap.TryGetValue(potentialHashMatch, out firstMatches))
  131. {
  132. continue;
  133. }
  134. var bestScore = -1;
  135. RestPath bestMatch = null;
  136. foreach (var restPath in firstMatches)
  137. {
  138. var score = restPath.MatchScore(httpMethod, matchUsingPathParts);
  139. if (score > bestScore)
  140. {
  141. bestScore = score;
  142. bestMatch = restPath;
  143. }
  144. }
  145. if (bestScore > 0 && bestMatch != null)
  146. {
  147. return bestMatch;
  148. }
  149. }
  150. return null;
  151. }
  152. public Task<object> Execute(HttpListenerHost httpHost, object requestDto, IRequest req)
  153. {
  154. var requestType = requestDto.GetType();
  155. req.OperationName = requestType.Name;
  156. var serviceType = httpHost.GetServiceTypeByRequest(requestType);
  157. var service = httpHost.CreateInstance(serviceType);
  158. if (service is IRequiresRequest serviceRequiresContext)
  159. {
  160. serviceRequiresContext.Request = req;
  161. }
  162. // Executes the service and returns the result
  163. return ServiceExecGeneral.Execute(serviceType, req, service, requestDto, requestType.GetMethodName());
  164. }
  165. }
  166. }