PageRenderTime 63ms CodeModel.GetById 36ms RepoModel.GetById 0ms app.codeStats 0ms

/src/ServiceStack/ServiceHost/ServiceController.cs

https://github.com/letssellsomebananas/ServiceStack
C# | 370 lines | 293 code | 74 blank | 3 comment | 29 complexity | c371d0f07123af8b32ff587f7ef554f9 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq.Expressions;
  4. using System.Reflection;
  5. using System.Text;
  6. using ServiceStack.Configuration;
  7. using ServiceStack.Logging;
  8. using ServiceStack.ServiceModel.Serialization;
  9. using ServiceStack.Text;
  10. namespace ServiceStack.ServiceHost
  11. {
  12. public class ServiceController
  13. : IServiceController
  14. {
  15. private static readonly ILog Log = LogManager.GetLogger(typeof(ServiceController));
  16. private const string ResponseDtoSuffix = "Response";
  17. public ServiceController(Func<IEnumerable<Type>> resolveServicesFn)
  18. {
  19. this.AllOperationTypes = new List<Type>();
  20. this.OperationTypes = new List<Type>();
  21. this.ServiceTypes = new HashSet<Type>();
  22. this.EnableAccessRestrictions = true;
  23. this.routes = new ServiceRoutes();
  24. this.ResolveServicesFn = resolveServicesFn;
  25. }
  26. readonly Dictionary<Type, Func<IRequestContext, object, object>> requestExecMap
  27. = new Dictionary<Type, Func<IRequestContext, object, object>>();
  28. readonly Dictionary<Type, ServiceAttribute> requestServiceAttrs
  29. = new Dictionary<Type, ServiceAttribute>();
  30. private readonly ServiceRoutes routes;
  31. public bool EnableAccessRestrictions { get; set; }
  32. public IList<Type> AllOperationTypes { get; protected set; }
  33. public IList<Type> OperationTypes { get; protected set; }
  34. public HashSet<Type> ServiceTypes { get; protected set; }
  35. public string DefaultOperationsNamespace { get; set; }
  36. public IServiceRoutes Routes { get { return routes; } }
  37. public Func<IEnumerable<Type>> ResolveServicesFn { get; set; }
  38. public void Register<TReq>(Func<IService<TReq>> invoker)
  39. {
  40. var requestType = typeof(TReq);
  41. Func<IRequestContext, object, object> handlerFn = (requestContext, dto) =>
  42. {
  43. var service = invoker();
  44. InjectRequestContext(service, requestContext);
  45. return ServiceExec<TReq>.Execute(
  46. service, (TReq)dto,
  47. requestContext != null ? requestContext.EndpointAttributes : EndpointAttributes.None);
  48. };
  49. requestExecMap.Add(requestType, handlerFn);
  50. }
  51. public void Register(ITypeFactory serviceFactoryFn)
  52. {
  53. foreach (var serviceType in ResolveServicesFn())
  54. {
  55. if (serviceType.IsAbstract || serviceType.ContainsGenericParameters) continue;
  56. foreach (var service in serviceType.GetInterfaces())
  57. {
  58. if (!service.IsGenericType
  59. || service.GetGenericTypeDefinition() != typeof(IService<>)
  60. ) continue;
  61. var requestType = service.GetGenericArguments()[0];
  62. Register(requestType, serviceType, serviceFactoryFn);
  63. RegisterRestPaths(requestType);
  64. this.ServiceTypes.Add(serviceType);
  65. this.AllOperationTypes.Add(requestType);
  66. this.OperationTypes.Add(requestType);
  67. var responseTypeName = requestType.FullName + ResponseDtoSuffix;
  68. var responseType = AssemblyUtils.FindType(responseTypeName);
  69. if (responseType != null)
  70. {
  71. this.AllOperationTypes.Add(responseType);
  72. this.OperationTypes.Add(responseType);
  73. }
  74. Log.DebugFormat("Registering {0} service '{1}' with request '{2}'",
  75. (responseType != null ? "SyncReply" : "OneWay"),
  76. serviceType.Name, requestType.Name);
  77. }
  78. }
  79. }
  80. public readonly Dictionary<string, List<RestPath>> RestPathMap = new Dictionary<string, List<RestPath>>();
  81. public void RegisterRestPaths(Type requestType)
  82. {
  83. var attrs = requestType.GetCustomAttributes(typeof(RestServiceAttribute), true);
  84. foreach (RestServiceAttribute attr in attrs)
  85. {
  86. var restPath = new RestPath(requestType, attr.Path, attr.Verbs, attr.DefaultContentType);
  87. if (!restPath.IsValid)
  88. throw new NotSupportedException(string.Format(
  89. "RestPath '{0}' on Type '{1}' is not Valid", attr.Path, requestType.Name));
  90. RegisterRestPath(restPath);
  91. }
  92. }
  93. public void RegisterRestPath(RestPath restPath)
  94. {
  95. List<RestPath> pathsAtFirstMatch;
  96. if (!RestPathMap.TryGetValue(restPath.FirstMatchHashKey, out pathsAtFirstMatch))
  97. {
  98. pathsAtFirstMatch = new List<RestPath>();
  99. RestPathMap[restPath.FirstMatchHashKey] = pathsAtFirstMatch;
  100. }
  101. pathsAtFirstMatch.Add(restPath);
  102. }
  103. public void AfterInit()
  104. {
  105. foreach (var restPath in this.routes.RestPaths)
  106. {
  107. RegisterRestPath(restPath);
  108. }
  109. }
  110. public IRestPath GetRestPathForRequest(string httpMethod, string pathInfo)
  111. {
  112. var matchUsingPathParts = RestPath.GetPathPartsForMatching(pathInfo);
  113. List<RestPath> firstMatches;
  114. var yieldedHashMatches = RestPath.GetFirstMatchHashKeys(matchUsingPathParts);
  115. foreach (var potentialHashMatch in yieldedHashMatches)
  116. {
  117. if (!this.RestPathMap.TryGetValue(potentialHashMatch, out firstMatches)) continue;
  118. foreach (var restPath in firstMatches)
  119. {
  120. if (restPath.IsMatch(httpMethod, matchUsingPathParts)) return restPath;
  121. }
  122. }
  123. var yieldedWildcardMatches = RestPath.GetFirstMatchWildCardHashKeys(matchUsingPathParts);
  124. foreach (var potentialHashMatch in yieldedWildcardMatches)
  125. {
  126. if (!this.RestPathMap.TryGetValue(potentialHashMatch, out firstMatches)) continue;
  127. foreach (var restPath in firstMatches)
  128. {
  129. if (restPath.IsMatch(httpMethod, matchUsingPathParts)) return restPath;
  130. }
  131. }
  132. return null;
  133. }
  134. internal class TypeFactoryWrapper : ITypeFactory
  135. {
  136. private readonly Func<Type, object> typeCreator;
  137. public TypeFactoryWrapper(Func<Type, object> typeCreator)
  138. {
  139. this.typeCreator = typeCreator;
  140. }
  141. public object CreateInstance(Type type)
  142. {
  143. return typeCreator(type);
  144. }
  145. }
  146. public void Register(Type requestType, Type serviceType)
  147. {
  148. var handlerFactoryFn = Expression.Lambda<Func<Type, object>>
  149. (
  150. Expression.New(serviceType),
  151. Expression.Parameter(typeof(Type), "serviceType")
  152. ).Compile();
  153. Register(requestType, serviceType, new TypeFactoryWrapper(handlerFactoryFn));
  154. }
  155. public void Register(Type requestType, Type serviceType, Func<Type, object> handlerFactoryFn)
  156. {
  157. Register(requestType, serviceType, new TypeFactoryWrapper(handlerFactoryFn));
  158. }
  159. public void Register(Type requestType, Type serviceType, ITypeFactory serviceFactoryFn)
  160. {
  161. var typeFactoryFn = CallServiceExecuteGeneric(requestType, serviceType);
  162. Func<IRequestContext, object, object> handlerFn = (requestContext, dto) =>
  163. {
  164. var service = serviceFactoryFn.CreateInstance(serviceType);
  165. using (service as IDisposable) // using is happy if this expression evals to null
  166. {
  167. InjectRequestContext(service, requestContext);
  168. var endpointAttrs = requestContext != null
  169. ? requestContext.EndpointAttributes
  170. : EndpointAttributes.None;
  171. try
  172. {
  173. //Executes the service and returns the result
  174. return typeFactoryFn(dto, service, endpointAttrs);
  175. }
  176. catch (TargetInvocationException tex)
  177. {
  178. //Mono invokes using reflection
  179. throw tex.InnerException ?? tex;
  180. }
  181. }
  182. };
  183. try
  184. {
  185. requestExecMap.Add(requestType, handlerFn);
  186. }
  187. catch (ArgumentException)
  188. {
  189. throw new AmbiguousMatchException(
  190. string.Format("Could not register the service '{0}' as another service with the definition of type 'IService<{1}>' already exists.",
  191. serviceType.FullName, requestType.Name));
  192. }
  193. var serviceAttrs = requestType.GetCustomAttributes(typeof(ServiceAttribute), false);
  194. if (serviceAttrs.Length > 0)
  195. {
  196. requestServiceAttrs.Add(requestType, (ServiceAttribute)serviceAttrs[0]);
  197. }
  198. }
  199. private static void InjectRequestContext(object service, IRequestContext requestContext)
  200. {
  201. if (requestContext == null) return;
  202. var serviceRequiresContext = service as IRequiresRequestContext;
  203. if (serviceRequiresContext != null)
  204. {
  205. serviceRequiresContext.RequestContext = requestContext;
  206. }
  207. }
  208. private static Func<object, object, EndpointAttributes, object> CallServiceExecuteGeneric(
  209. Type requestType, Type serviceType)
  210. {
  211. try
  212. {
  213. var requestDtoParam = Expression.Parameter(typeof(object), "requestDto");
  214. var requestDtoStrong = Expression.Convert(requestDtoParam, requestType);
  215. var serviceParam = Expression.Parameter(typeof(object), "serviceObj");
  216. var serviceStrong = Expression.Convert(serviceParam, serviceType);
  217. var attrsParam = Expression.Parameter(typeof(EndpointAttributes), "attrs");
  218. var mi = ServiceExec.GetExecMethodInfo(serviceType, requestType);
  219. Expression callExecute = Expression.Call(
  220. mi, new Expression[] { serviceStrong, requestDtoStrong, attrsParam });
  221. var executeFunc = Expression.Lambda<Func<object, object, EndpointAttributes, object>>
  222. (callExecute, requestDtoParam, serviceParam, attrsParam).Compile();
  223. return executeFunc;
  224. }
  225. catch (Exception)
  226. {
  227. //problems with MONO, using reflection for temp fix
  228. return delegate(object request, object service, EndpointAttributes attrs)
  229. {
  230. var mi = ServiceExec.GetExecMethodInfo(serviceType, requestType);
  231. return mi.Invoke(null, new[] { service, request, attrs });
  232. };
  233. }
  234. }
  235. public object Execute(object dto)
  236. {
  237. return Execute(dto, null);
  238. }
  239. public object Execute(object request, IRequestContext requestContext)
  240. {
  241. var requestType = request.GetType();
  242. if (EnableAccessRestrictions)
  243. {
  244. AssertServiceRestrictions(requestType,
  245. requestContext != null ? requestContext.EndpointAttributes : EndpointAttributes.None);
  246. }
  247. var handlerFn = GetService(requestType);
  248. return handlerFn(requestContext, request);
  249. }
  250. public Func<IRequestContext, object, object> GetService(Type requestType)
  251. {
  252. Func<IRequestContext, object, object> handlerFn;
  253. if (!requestExecMap.TryGetValue(requestType, out handlerFn))
  254. {
  255. throw new NotImplementedException(
  256. string.Format("Unable to resolve service '{0}'", requestType.Name));
  257. }
  258. return handlerFn;
  259. }
  260. public object ExecuteText(string requestXml, Type requestType, IRequestContext requestContext)
  261. {
  262. var request = DataContractDeserializer.Instance.Parse(requestXml, requestType);
  263. var response = Execute(request, requestContext);
  264. var responseXml = DataContractSerializer.Instance.Parse(response);
  265. return responseXml;
  266. }
  267. public void AssertServiceRestrictions(Type requestType, EndpointAttributes actualAttributes)
  268. {
  269. ServiceAttribute serviceAttr;
  270. var hasNoAccessRestrictions = !requestServiceAttrs.TryGetValue(requestType, out serviceAttr)
  271. || serviceAttr.HasNoAccessRestrictions;
  272. if (hasNoAccessRestrictions)
  273. {
  274. return;
  275. }
  276. var failedScenarios = new StringBuilder();
  277. foreach (var requiredScenario in serviceAttr.RestrictAccessToScenarios)
  278. {
  279. var allServiceRestrictionsMet = (requiredScenario & actualAttributes) == actualAttributes;
  280. if (allServiceRestrictionsMet)
  281. {
  282. return;
  283. }
  284. var passed = requiredScenario & actualAttributes;
  285. var failed = requiredScenario & ~(passed);
  286. failedScenarios.AppendFormat("\n -[{0}]", failed);
  287. }
  288. var internalDebugMsg = (EndpointAttributes.InternalNetworkAccess & actualAttributes) != 0
  289. ? "\n Unauthorized call was made from: " + actualAttributes
  290. : "";
  291. throw new UnauthorizedAccessException(
  292. string.Format("Could not execute service '{0}', The following restrictions were not met: '{1}'" + internalDebugMsg,
  293. requestType.Name, failedScenarios));
  294. }
  295. }
  296. }