ServiceController.cs 6.7 KB

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