diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index b4bb92cfa..b1601df05 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -215,6 +215,13 @@ + + + + + + + diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 322cdf4f0..99ec146d7 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -3,7 +3,6 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Logging; using ServiceStack; -using ServiceStack.Host; using System; using System.Collections.Generic; using System.IO; @@ -13,6 +12,7 @@ using System.Text; using System.Threading.Tasks; using Emby.Server.Implementations.HttpServer; using Emby.Server.Implementations.HttpServer.SocketSharp; +using Emby.Server.Implementations.Services; using MediaBrowser.Common.Net; using MediaBrowser.Common.Security; using MediaBrowser.Controller; @@ -61,12 +61,15 @@ namespace Emby.Server.Implementations.HttpServer private readonly Func> _funcParseFn; private readonly bool _enableDualModeSockets; + public List> RequestFilters { get; set; } + private Dictionary ServiceOperationsMap = new Dictionary(); + public HttpListenerHost(IServerApplicationHost applicationHost, ILogger logger, IServerConfigurationManager config, string serviceName, string defaultRedirectPath, INetworkManager networkManager, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IJsonSerializer jsonSerializer, IXmlSerializer xmlSerializer, IEnvironmentInfo environment, ICertificate certificate, IStreamFactory streamFactory, Func> funcParseFn, bool enableDualModeSockets) - : base(serviceName) + : base() { _appHost = applicationHost; DefaultRedirectPath = defaultRedirectPath; @@ -85,6 +88,8 @@ namespace Emby.Server.Implementations.HttpServer _config = config; _logger = logger; + + RequestFilters = new List>(); } public string GlobalResponse { get; set; } @@ -99,18 +104,7 @@ namespace Emby.Server.Implementations.HttpServer {typeof (ArgumentException), 400} }; - public override void Configure() - { - var requestFilters = _appHost.GetExports().ToList(); - foreach (var filter in requestFilters) - { - GlobalRequestFilters.Add(filter.Filter); - } - - GlobalResponseFilters.Add(new ResponseFilter(_logger).FilterResponse); - } - - protected override ILogger Logger + protected ILogger Logger { get { @@ -118,32 +112,73 @@ namespace Emby.Server.Implementations.HttpServer } } - public override T Resolve() - { - return _appHost.Resolve(); - } - - public override T TryResolve() - { - return _appHost.TryResolve(); - } - public override object CreateInstance(Type type) { return _appHost.CreateInstance(type); } - protected override ServiceController CreateServiceController() + private ServiceController CreateServiceController() { var types = _restServices.Select(r => r.GetType()).ToArray(); return new ServiceController(() => types); } - public override ServiceStackHost Start(string listeningAtUrlBase) + /// + /// Applies the request filters. Returns whether or not the request has been handled + /// and no more processing should be done. + /// + /// + public void ApplyRequestFilters(IRequest req, IResponse res, object requestDto) { - StartListener(); - return this; + //Exec all RequestFilter attributes with Priority < 0 + var attributes = GetRequestFilterAttributes(requestDto.GetType()); + var i = 0; + for (; i < attributes.Length && attributes[i].Priority < 0; i++) + { + var attribute = attributes[i]; + attribute.RequestFilter(req, res, requestDto); + } + + //Exec global filters + foreach (var requestFilter in RequestFilters) + { + requestFilter(req, res, requestDto); + } + + //Exec remaining RequestFilter attributes with Priority >= 0 + for (; i < attributes.Length && attributes[i].Priority >= 0; i++) + { + var attribute = attributes[i]; + attribute.RequestFilter(req, res, requestDto); + } + } + + public Type GetServiceTypeByRequest(Type requestType) + { + Type serviceType; + ServiceOperationsMap.TryGetValue(requestType, out serviceType); + return serviceType; + } + + public void AddServiceInfo(Type serviceType, Type requestType, Type responseType) + { + ServiceOperationsMap[requestType] = serviceType; + } + + private IHasRequestFilter[] GetRequestFilterAttributes(Type requestDtoType) + { + var attributes = requestDtoType.AllAttributes().OfType().ToList(); + + var serviceType = GetServiceTypeByRequest(requestDtoType); + if (serviceType != null) + { + attributes.AddRange(serviceType.AllAttributes().OfType()); + } + + attributes.Sort((x, y) => x.Priority - y.Priority); + + return attributes.ToArray(); } /// @@ -531,11 +566,11 @@ namespace Emby.Server.Implementations.HttpServer return; } - var handler = HttpHandlerFactory.GetHandler(httpReq, _logger); + var handler = GetServiceHandler(httpReq); if (handler != null) { - await handler.ProcessRequestAsync(httpReq, httpRes, operationName).ConfigureAwait(false); + await handler.ProcessRequestAsync(this, httpReq, httpRes, Logger, operationName).ConfigureAwait(false); } else { @@ -565,6 +600,35 @@ namespace Emby.Server.Implementations.HttpServer } } + // Entry point for HttpListener + public ServiceHandler GetServiceHandler(IHttpRequest httpReq) + { + var pathInfo = httpReq.PathInfo; + + var pathParts = pathInfo.TrimStart('/').Split('/'); + if (pathParts.Length == 0) + { + _logger.Error("Path parts empty for PathInfo: {0}, Url: {1}", pathInfo, httpReq.RawUrl); + return null; + } + + string contentType; + var restPath = ServiceHandler.FindMatchingRestPath(httpReq.HttpMethod, pathInfo, _logger, out contentType); + + if (restPath != null) + { + return new ServiceHandler + { + RestPath = restPath, + ResponseContentType = contentType + }; + } + + _logger.Error("Could not find handler for {0}", pathInfo); + return null; + } + + private void Write(IResponse response, string text) { var bOutput = Encoding.UTF8.GetBytes(text); @@ -580,6 +644,7 @@ namespace Emby.Server.Implementations.HttpServer httpRes.AddHeader("Location", url); } + public ServiceController ServiceController { get; private set; } /// /// Adds the rest handlers. @@ -593,12 +658,22 @@ namespace Emby.Server.Implementations.HttpServer _logger.Info("Calling ServiceStack AppHost.Init"); - base.Init(); + Instance = this; + + ServiceController.Init(this); + + var requestFilters = _appHost.GetExports().ToList(); + foreach (var filter in requestFilters) + { + RequestFilters.Add(filter.Filter); + } + + GlobalResponseFilters.Add(new ResponseFilter(_logger).FilterResponse); } public override RouteAttribute[] GetRouteAttributes(Type requestType) { - var routes = base.GetRouteAttributes(requestType).ToList(); + var routes = requestType.AllAttributes(); var clone = routes.ToList(); foreach (var route in clone) @@ -628,33 +703,6 @@ namespace Emby.Server.Implementations.HttpServer return routes.ToArray(); } - public override object GetTaskResult(Task task, string requestName) - { - try - { - var taskObject = task as Task; - if (taskObject != null) - { - return taskObject.Result; - } - - task.Wait(); - - var type = task.GetType().GetTypeInfo(); - if (!type.IsGenericType) - { - return null; - } - - Logger.Warn("Getting task result from " + requestName + " using reflection. For better performance have your api return Task"); - return type.GetDeclaredProperty("Result").GetValue(task); - } - catch (TypeAccessException) - { - return null; //return null for void Task's - } - } - public override Func GetParseFn(Type propertyType) { return _funcParseFn(propertyType); @@ -740,7 +788,7 @@ namespace Emby.Server.Implementations.HttpServer public void StartServer(IEnumerable urlPrefixes) { UrlPrefixes = urlPrefixes.ToList(); - Start(UrlPrefixes.First()); + StartListener(); } } } \ No newline at end of file diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index e78446bc8..3f756fc7a 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -13,10 +13,10 @@ using System.Text; using System.Threading.Tasks; using System.Xml; using Emby.Server.Implementations.HttpServer; +using Emby.Server.Implementations.Services; using MediaBrowser.Model.IO; using MediaBrowser.Model.Services; using ServiceStack; -using ServiceStack.Host; using IRequest = MediaBrowser.Model.Services.IRequest; using MimeTypes = MediaBrowser.Model.Net.MimeTypes; using StreamWriter = Emby.Server.Implementations.HttpServer.StreamWriter; @@ -203,7 +203,11 @@ namespace Emby.Server.Implementations.HttpServer // Do not use the memoryStreamFactory here, they don't place nice with compression using (var ms = new MemoryStream()) { - ContentTypes.Instance.SerializeToStream(request, dto, ms); + var contentType = request.ResponseContentType; + var writerFn = RequestHelper.GetResponseWriter(contentType); + + writerFn(dto, ms); + ms.Position = 0; var responseHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); diff --git a/ServiceStack/HttpResult.cs b/Emby.Server.Implementations/Services/HttpResult.cs similarity index 88% rename from ServiceStack/HttpResult.cs rename to Emby.Server.Implementations/Services/HttpResult.cs index 3f86ffdf7..585c3e4f8 100644 --- a/ServiceStack/HttpResult.cs +++ b/Emby.Server.Implementations/Services/HttpResult.cs @@ -1,14 +1,12 @@ -using System; using System.Collections.Generic; using System.IO; using System.Net; -using System.Text; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.Services; -using ServiceStack.Host; +using ServiceStack; -namespace ServiceStack +namespace Emby.Server.Implementations.Services { public class HttpResult : IHttpResult, IAsyncStreamWriter @@ -55,7 +53,7 @@ namespace ServiceStack return; } - await HttpResponseExtensionsInternal.WriteObject(this.RequestContext, this.Response, response).ConfigureAwait(false); + await ResponseHelper.WriteObject(this.RequestContext, this.Response, response).ConfigureAwait(false); } } } diff --git a/ServiceStack/Host/ContentTypes.cs b/Emby.Server.Implementations/Services/RequestHelper.cs similarity index 57% rename from ServiceStack/Host/ContentTypes.cs rename to Emby.Server.Implementations/Services/RequestHelper.cs index f7734a36b..8cfc3d089 100644 --- a/ServiceStack/Host/ContentTypes.cs +++ b/Emby.Server.Implementations/Services/RequestHelper.cs @@ -1,43 +1,14 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Text; -using MediaBrowser.Model.Services; +using ServiceStack; -namespace ServiceStack.Host +namespace Emby.Server.Implementations.Services { - public class ContentTypes + public class RequestHelper { - public static ContentTypes Instance = new ContentTypes(); - - public void SerializeToStream(IRequest req, object response, Stream responseStream) + public static Func GetRequestReader(string contentType) { - var contentType = req.ResponseContentType; - var serializer = GetStreamSerializer(contentType); - - serializer(response, responseStream); - } - - public static Action GetStreamSerializer(string contentType) - { - switch (GetRealContentType(contentType)) - { - case "application/xml": - case "text/xml": - case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml - return (o, s) => ServiceStackHost.Instance.SerializeToXml(o, s); - - case "application/json": - case "text/json": - return (o, s) => ServiceStackHost.Instance.SerializeToJson(o, s); - } - - return null; - } - - public Func GetStreamDeserializer(string contentType) - { - switch (GetRealContentType(contentType)) + switch (GetContentTypeWithoutEncoding(contentType)) { case "application/xml": case "text/xml": @@ -52,7 +23,24 @@ namespace ServiceStack.Host return null; } - private static string GetRealContentType(string contentType) + public static Action GetResponseWriter(string contentType) + { + switch (GetContentTypeWithoutEncoding(contentType)) + { + case "application/xml": + case "text/xml": + case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml + return (o, s) => ServiceStackHost.Instance.SerializeToXml(o, s); + + case "application/json": + case "text/json": + return (o, s) => ServiceStackHost.Instance.SerializeToJson(o, s); + } + + return null; + } + + private static string GetContentTypeWithoutEncoding(string contentType) { return contentType == null ? null diff --git a/ServiceStack/HttpResponseExtensionsInternal.cs b/Emby.Server.Implementations/Services/ResponseHelper.cs similarity index 82% rename from ServiceStack/HttpResponseExtensionsInternal.cs rename to Emby.Server.Implementations/Services/ResponseHelper.cs index feb18081a..1af70ad7f 100644 --- a/ServiceStack/HttpResponseExtensionsInternal.cs +++ b/Emby.Server.Implementations/Services/ResponseHelper.cs @@ -1,21 +1,17 @@ -//Copyright (c) Service Stack LLC. All Rights Reserved. -//License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt - using System; +using System.Collections.Generic; using System.IO; using System.Net; -using System.Threading.Tasks; -using System.Collections.Generic; using System.Text; using System.Threading; +using System.Threading.Tasks; using MediaBrowser.Model.Services; -using ServiceStack.Host; -namespace ServiceStack +namespace Emby.Server.Implementations.Services { - public static class HttpResponseExtensionsInternal + public static class ResponseHelper { - public static async Task WriteToOutputStream(IResponse response, object result) + private static async Task WriteToOutputStream(IResponse response, object result) { var asyncStreamWriter = result as IAsyncStreamWriter; if (asyncStreamWriter != null) @@ -54,24 +50,16 @@ namespace ServiceStack return false; } - /// - /// End a ServiceStack Request with no content - /// - public static void EndRequestWithNoContent(this IResponse httpRes) - { - if (httpRes.StatusCode == (int)HttpStatusCode.OK) - { - httpRes.StatusCode = (int)HttpStatusCode.NoContent; - } - - httpRes.SetContentLength(0); - } - - public static Task WriteToResponse(this IResponse httpRes, IRequest httpReq, object result) + public static Task WriteToResponse(IResponse httpRes, IRequest httpReq, object result) { if (result == null) { - httpRes.EndRequestWithNoContent(); + if (httpRes.StatusCode == (int)HttpStatusCode.OK) + { + httpRes.StatusCode = (int)HttpStatusCode.NoContent; + } + + httpRes.SetContentLength(0); return Task.FromResult(true); } @@ -80,10 +68,10 @@ namespace ServiceStack { httpResult.RequestContext = httpReq; httpReq.ResponseContentType = httpResult.ContentType ?? httpReq.ResponseContentType; - return httpRes.WriteToResponseInternal(httpResult, httpReq); + return WriteToResponseInternal(httpRes, httpResult, httpReq); } - return httpRes.WriteToResponseInternal(result, httpReq); + return WriteToResponseInternal(httpRes, result, httpReq); } /// @@ -94,7 +82,7 @@ namespace ServiceStack /// Whether or not it was implicity handled by ServiceStack's built-in handlers. /// The serialization context. /// - private static async Task WriteToResponseInternal(this IResponse response, object result, IRequest request) + private static async Task WriteToResponseInternal(IResponse response, object result, IRequest request) { var defaultContentType = request.ResponseContentType; @@ -173,7 +161,7 @@ namespace ServiceStack public static async Task WriteObject(IRequest request, object result, IResponse response) { var contentType = request.ResponseContentType; - var serializer = ContentTypes.GetStreamSerializer(contentType); + var serializer = RequestHelper.GetResponseWriter(contentType); using (var ms = new MemoryStream()) { diff --git a/ServiceStack/Host/ServiceController.cs b/Emby.Server.Implementations/Services/ServiceController.cs similarity index 77% rename from ServiceStack/Host/ServiceController.cs rename to Emby.Server.Implementations/Services/ServiceController.cs index 158097dd0..714a16df5 100644 --- a/ServiceStack/Host/ServiceController.cs +++ b/Emby.Server.Implementations/Services/ServiceController.cs @@ -1,14 +1,13 @@ using System; -using System.Collections; using System.Collections.Generic; -using System.Linq; using System.Reflection; -using System.Text; using System.Threading.Tasks; +using Emby.Server.Implementations.HttpServer; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Services; +using ServiceStack; -namespace ServiceStack.Host +namespace Emby.Server.Implementations.Services { public delegate Task InstanceExecFn(IRequest requestContext, object intance, object request); public delegate object ActionInvokerFn(object intance, object request); @@ -16,21 +15,20 @@ namespace ServiceStack.Host public class ServiceController { + public static ServiceController Instance; private readonly Func> _resolveServicesFn; public ServiceController(Func> resolveServicesFn) { + Instance = this; _resolveServicesFn = resolveServicesFn; - this.RequestTypeFactoryMap = new Dictionary>(); } - public Dictionary> RequestTypeFactoryMap { get; set; } - - public void Init() + public void Init(HttpListenerHost appHost) { foreach (var serviceType in _resolveServicesFn()) { - RegisterService(serviceType); + RegisterService(appHost, serviceType); } } @@ -41,15 +39,12 @@ namespace ServiceStack.Host : type.GetTypeInfo().GenericTypeArguments; } - public void RegisterService(Type serviceType) + public void RegisterService(HttpListenerHost appHost, Type serviceType) { var processedReqs = new HashSet(); var actions = ServiceExecGeneral.Reset(serviceType); - var requiresRequestStreamTypeInfo = typeof(IRequiresRequestStream).GetTypeInfo(); - - var appHost = ServiceStackHost.Instance; foreach (var mi in serviceType.GetActions()) { var requestType = mi.GetParameters()[0].ParameterType; @@ -67,20 +62,7 @@ namespace ServiceStack.Host RegisterRestPaths(requestType); - appHost.Metadata.Add(serviceType, requestType, responseType); - - if (requiresRequestStreamTypeInfo.IsAssignableFrom(requestType.GetTypeInfo())) - { - this.RequestTypeFactoryMap[requestType] = req => - { - var restPath = req.GetRoute(); - var request = RestHandler.CreateRequest(req, restPath, req.GetRequestParams(), ServiceStackHost.Instance.CreateInstance(requestType)); - - var rawReq = (IRequiresRequestStream)request; - rawReq.RequestStream = req.InputStream; - return rawReq; - }; - } + appHost.AddServiceInfo(serviceType, requestType, responseType); } } @@ -121,21 +103,6 @@ namespace ServiceStack.Host pathsAtFirstMatch.Add(restPath); } - public void AfterInit() - { - var appHost = ServiceStackHost.Instance; - - //Register any routes configured on Metadata.Routes - foreach (var restPath in appHost.RestPaths) - { - RegisterRestPath(restPath); - } - - //Sync the RestPaths collections - appHost.RestPaths.Clear(); - appHost.RestPaths.AddRange(RestPathMap.Values.SelectMany(x => x)); - } - public RestPath GetRestPathForRequest(string httpMethod, string pathInfo, ILogger logger) { var matchUsingPathParts = RestPath.GetPathPartsForMatching(pathInfo); @@ -191,15 +158,15 @@ namespace ServiceStack.Host return null; } - public async Task Execute(object requestDto, IRequest req) + public async Task Execute(HttpListenerHost appHost, object requestDto, IRequest req) { req.Dto = requestDto; var requestType = requestDto.GetType(); req.OperationName = requestType.Name; - var serviceType = ServiceStackHost.Instance.Metadata.GetServiceTypeByRequest(requestType); + var serviceType = appHost.GetServiceTypeByRequest(requestType); - var service = ServiceStackHost.Instance.CreateInstance(serviceType); + var service = appHost.CreateInstance(serviceType); //var service = typeFactory.CreateInstance(serviceType); diff --git a/ServiceStack/Host/ServiceExec.cs b/Emby.Server.Implementations/Services/ServiceExec.cs similarity index 70% rename from ServiceStack/Host/ServiceExec.cs rename to Emby.Server.Implementations/Services/ServiceExec.cs index cb501a3ad..59af3078f 100644 --- a/ServiceStack/Host/ServiceExec.cs +++ b/Emby.Server.Implementations/Services/ServiceExec.cs @@ -1,6 +1,3 @@ -//Copyright (c) Service Stack LLC. All Rights Reserved. -//License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt - using System; using System.Collections.Generic; using System.Linq; @@ -8,11 +5,25 @@ using System.Linq.Expressions; using System.Reflection; using System.Threading.Tasks; using MediaBrowser.Model.Services; +using ServiceStack; -namespace ServiceStack.Host +namespace Emby.Server.Implementations.Services { public static class ServiceExecExtensions { + public static HashSet AllVerbs = new HashSet(new[] { + "OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT", // RFC 2616 + "PROPFIND", "PROPPATCH", "MKCOL", "COPY", "MOVE", "LOCK", "UNLOCK", // RFC 2518 + "VERSION-CONTROL", "REPORT", "CHECKOUT", "CHECKIN", "UNCHECKOUT", + "MKWORKSPACE", "UPDATE", "LABEL", "MERGE", "BASELINE-CONTROL", "MKACTIVITY", // RFC 3253 + "ORDERPATCH", // RFC 3648 + "ACL", // RFC 3744 + "PATCH", // https://datatracker.ietf.org/doc/draft-dusseault-http-patch/ + "SEARCH", // https://datatracker.ietf.org/doc/draft-reschke-webdav-search/ + "BCOPY", "BDELETE", "BMOVE", "BPROPFIND", "BPROPPATCH", "NOTIFY", + "POLL", "SUBSCRIBE", "UNSUBSCRIBE" + }); + public static IEnumerable GetActions(this Type serviceType) { foreach (var mi in serviceType.GetRuntimeMethods().Where(i => i.IsPublic && !i.IsStatic)) @@ -20,8 +31,8 @@ namespace ServiceStack.Host if (mi.GetParameters().Length != 1) continue; - var actionName = mi.Name.ToUpper(); - if (!HttpMethods.AllVerbs.Contains(actionName) && actionName != ActionContext.AnyAction) + var actionName = mi.Name; + if (!AllVerbs.Contains(actionName, StringComparer.OrdinalIgnoreCase) && !string.Equals(actionName, ServiceMethod.AnyAction, StringComparison.OrdinalIgnoreCase)) continue; yield return mi; @@ -31,9 +42,9 @@ namespace ServiceStack.Host internal static class ServiceExecGeneral { - public static Dictionary execMap = new Dictionary(); + public static Dictionary execMap = new Dictionary(); - public static void CreateServiceRunnersFor(Type requestType, List actions) + public static void CreateServiceRunnersFor(Type requestType, List actions) { foreach (var actionCtx in actions) { @@ -45,12 +56,11 @@ namespace ServiceStack.Host public static async Task Execute(Type serviceType, IRequest request, object instance, object requestDto, string requestName) { - var actionName = request.Verb - ?? HttpMethods.Post; //MQ Services + var actionName = request.Verb ?? "POST"; - ActionContext actionContext; - if (ServiceExecGeneral.execMap.TryGetValue(ActionContext.Key(serviceType, actionName, requestName), out actionContext) - || ServiceExecGeneral.execMap.TryGetValue(ActionContext.AnyKey(serviceType, requestName), out actionContext)) + ServiceMethod actionContext; + if (ServiceExecGeneral.execMap.TryGetValue(ServiceMethod.Key(serviceType, actionName, requestName), out actionContext) + || ServiceExecGeneral.execMap.TryGetValue(ServiceMethod.AnyKey(serviceType, requestName), out actionContext)) { if (actionContext.RequestFilters != null) { @@ -67,7 +77,7 @@ namespace ServiceStack.Host if (taskResponse != null) { await taskResponse.ConfigureAwait(false); - response = ServiceStackHost.Instance.GetTaskResult(taskResponse, requestName); + response = ServiceHandler.GetTaskResult(taskResponse); } return response; @@ -77,19 +87,19 @@ namespace ServiceStack.Host throw new NotImplementedException(string.Format("Could not find method named {1}({0}) or Any({0}) on Service {2}", requestDto.GetType().GetOperationName(), expectedMethodName, serviceType.GetOperationName())); } - public static List Reset(Type serviceType) + public static List Reset(Type serviceType) { - var actions = new List(); + var actions = new List(); foreach (var mi in serviceType.GetActions()) { - var actionName = mi.Name.ToUpper(); + var actionName = mi.Name; var args = mi.GetParameters(); var requestType = args[0].ParameterType; - var actionCtx = new ActionContext + var actionCtx = new ServiceMethod { - Id = ActionContext.Key(serviceType, actionName, requestType.GetOperationName()) + Id = ServiceMethod.Key(serviceType, actionName, requestType.GetOperationName()) }; try diff --git a/Emby.Server.Implementations/Services/ServiceHandler.cs b/Emby.Server.Implementations/Services/ServiceHandler.cs new file mode 100644 index 000000000..003776f9c --- /dev/null +++ b/Emby.Server.Implementations/Services/ServiceHandler.cs @@ -0,0 +1,297 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Threading.Tasks; +using Emby.Server.Implementations.HttpServer; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Services; +using ServiceStack; + +namespace Emby.Server.Implementations.Services +{ + public class ServiceHandler + { + public async Task HandleResponseAsync(object response) + { + var taskResponse = response as Task; + + if (taskResponse == null) + { + return response; + } + + await taskResponse.ConfigureAwait(false); + + var taskResult = GetTaskResult(taskResponse); + + var subTask = taskResult as Task; + if (subTask != null) + { + taskResult = GetTaskResult(subTask); + } + + return taskResult; + } + + internal static object GetTaskResult(Task task) + { + try + { + var taskObject = task as Task; + if (taskObject != null) + { + return taskObject.Result; + } + + task.Wait(); + + var type = task.GetType().GetTypeInfo(); + if (!type.IsGenericType) + { + return null; + } + + return type.GetDeclaredProperty("Result").GetValue(task); + } + catch (TypeAccessException) + { + return null; //return null for void Task's + } + } + + protected static object CreateContentTypeRequest(IRequest httpReq, Type requestType, string contentType) + { + if (!string.IsNullOrEmpty(contentType) && httpReq.ContentLength > 0) + { + var deserializer = RequestHelper.GetRequestReader(contentType); + if (deserializer != null) + { + return deserializer(requestType, httpReq.InputStream); + } + } + return ServiceStackHost.Instance.CreateInstance(requestType); //Return an empty DTO, even for empty request bodies + } + + public static RestPath FindMatchingRestPath(string httpMethod, string pathInfo, ILogger logger, out string contentType) + { + pathInfo = GetSanitizedPathInfo(pathInfo, out contentType); + + return ServiceController.Instance.GetRestPathForRequest(httpMethod, pathInfo, logger); + } + + public static string GetSanitizedPathInfo(string pathInfo, out string contentType) + { + contentType = null; + var pos = pathInfo.LastIndexOf('.'); + if (pos >= 0) + { + var format = pathInfo.Substring(pos + 1); + contentType = GetFormatContentType(format); + if (contentType != null) + { + pathInfo = pathInfo.Substring(0, pos); + } + } + return pathInfo; + } + + private static string GetFormatContentType(string format) + { + //built-in formats + if (format == "json") + return "application/json"; + if (format == "xml") + return "application/xml"; + + return null; + } + + public RestPath GetRestPath(string httpMethod, string pathInfo) + { + if (this.RestPath == null) + { + string contentType; + this.RestPath = FindMatchingRestPath(httpMethod, pathInfo, new NullLogger(), out contentType); + + if (contentType != null) + ResponseContentType = contentType; + } + return this.RestPath; + } + + public RestPath RestPath { get; set; } + + // Set from SSHHF.GetHandlerForPathInfo() + public string ResponseContentType { get; set; } + + public async Task ProcessRequestAsync(HttpListenerHost appHost, IRequest httpReq, IResponse httpRes, ILogger logger, string operationName) + { + var restPath = GetRestPath(httpReq.Verb, httpReq.PathInfo); + if (restPath == null) + { + throw new NotSupportedException("No RestPath found for: " + httpReq.Verb + " " + httpReq.PathInfo); + } + + SetRoute(httpReq, restPath); + + if (ResponseContentType != null) + httpReq.ResponseContentType = ResponseContentType; + + var request = httpReq.Dto = CreateRequest(httpReq, restPath, logger); + + appHost.ApplyRequestFilters(httpReq, httpRes, request); + + var rawResponse = await appHost.ServiceController.Execute(appHost, request, httpReq).ConfigureAwait(false); + + var response = await HandleResponseAsync(rawResponse).ConfigureAwait(false); + + // Apply response filters + foreach (var responseFilter in appHost.GlobalResponseFilters) + { + responseFilter(httpReq, httpRes, response); + } + + await ResponseHelper.WriteToResponse(httpRes, httpReq, response).ConfigureAwait(false); + } + + public static object CreateRequest(IRequest httpReq, RestPath restPath, ILogger logger) + { + var requestType = restPath.RequestType; + + if (RequireqRequestStream(requestType)) + { + // Used by IRequiresRequestStream + return CreateRequiresRequestStreamRequest(httpReq, requestType); + } + + var requestParams = GetFlattenedRequestParams(httpReq); + return CreateRequest(httpReq, restPath, requestParams); + } + + private static bool RequireqRequestStream(Type requestType) + { + var requiresRequestStreamTypeInfo = typeof(IRequiresRequestStream).GetTypeInfo(); + + return requiresRequestStreamTypeInfo.IsAssignableFrom(requestType.GetTypeInfo()); + } + + private static IRequiresRequestStream CreateRequiresRequestStreamRequest(IRequest req, Type requestType) + { + var restPath = GetRoute(req); + var request = ServiceHandler.CreateRequest(req, restPath, GetRequestParams(req), ServiceStackHost.Instance.CreateInstance(requestType)); + + var rawReq = (IRequiresRequestStream)request; + rawReq.RequestStream = req.InputStream; + return rawReq; + } + + public static object CreateRequest(IRequest httpReq, RestPath restPath, Dictionary requestParams) + { + var requestDto = CreateContentTypeRequest(httpReq, restPath.RequestType, httpReq.ContentType); + + return CreateRequest(httpReq, restPath, requestParams, requestDto); + } + + public static object CreateRequest(IRequest httpReq, RestPath restPath, Dictionary requestParams, object requestDto) + { + string contentType; + var pathInfo = !restPath.IsWildCardPath + ? GetSanitizedPathInfo(httpReq.PathInfo, out contentType) + : httpReq.PathInfo; + + return restPath.CreateRequest(pathInfo, requestParams, requestDto); + } + + /// + /// Duplicate Params are given a unique key by appending a #1 suffix + /// + private static Dictionary GetRequestParams(IRequest request) + { + var map = new Dictionary(); + + foreach (var name in request.QueryString.Keys) + { + if (name == null) continue; //thank you ASP.NET + + var values = request.QueryString.GetValues(name); + if (values.Length == 1) + { + map[name] = values[0]; + } + else + { + for (var i = 0; i < values.Length; i++) + { + map[name + (i == 0 ? "" : "#" + i)] = values[i]; + } + } + } + + if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT")) && request.FormData != null) + { + foreach (var name in request.FormData.Keys) + { + if (name == null) continue; //thank you ASP.NET + + var values = request.FormData.GetValues(name); + if (values.Length == 1) + { + map[name] = values[0]; + } + else + { + for (var i = 0; i < values.Length; i++) + { + map[name + (i == 0 ? "" : "#" + i)] = values[i]; + } + } + } + } + + return map; + } + + private static bool IsMethod(string method, string expected) + { + return string.Equals(method, expected, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Duplicate params have their values joined together in a comma-delimited string + /// + private static Dictionary GetFlattenedRequestParams(IRequest request) + { + var map = new Dictionary(); + + foreach (var name in request.QueryString.Keys) + { + if (name == null) continue; //thank you ASP.NET + map[name] = request.QueryString[name]; + } + + if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT")) && request.FormData != null) + { + foreach (var name in request.FormData.Keys) + { + if (name == null) continue; //thank you ASP.NET + map[name] = request.FormData[name]; + } + } + + return map; + } + + private static void SetRoute(IRequest req, RestPath route) + { + req.Items["__route"] = route; + } + + private static RestPath GetRoute(IRequest req) + { + object route; + req.Items.TryGetValue("__route", out route); + return route as RestPath; + } + } + +} diff --git a/ServiceStack/Host/ActionContext.cs b/Emby.Server.Implementations/Services/ServiceMethod.cs similarity index 81% rename from ServiceStack/Host/ActionContext.cs rename to Emby.Server.Implementations/Services/ServiceMethod.cs index 9f165cff1..bcbc6fb57 100644 --- a/ServiceStack/Host/ActionContext.cs +++ b/Emby.Server.Implementations/Services/ServiceMethod.cs @@ -1,11 +1,8 @@ using System; -namespace ServiceStack.Host +namespace Emby.Server.Implementations.Services { - /// - /// Context to capture IService action - /// - public class ActionContext + public class ServiceMethod { public const string AnyAction = "ANY"; diff --git a/ServiceStack/FilterAttributeCache.cs b/ServiceStack/FilterAttributeCache.cs deleted file mode 100644 index 378433add..000000000 --- a/ServiceStack/FilterAttributeCache.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Threading; -using ServiceStack; - -namespace ServiceStack.Support.WebHost -{ - public static class FilterAttributeCache - { - public static MediaBrowser.Model.Services.IHasRequestFilter[] GetRequestFilterAttributes(Type requestDtoType) - { - var attributes = requestDtoType.AllAttributes().OfType().ToList(); - - var serviceType = ServiceStackHost.Instance.Metadata.GetServiceTypeByRequest(requestDtoType); - if (serviceType != null) - { - attributes.AddRange(serviceType.AllAttributes().OfType()); - } - - attributes.Sort((x,y) => x.Priority - y.Priority); - - return attributes.ToArray(); - } - } -} diff --git a/ServiceStack/Host/RestHandler.cs b/ServiceStack/Host/RestHandler.cs deleted file mode 100644 index abc346869..000000000 --- a/ServiceStack/Host/RestHandler.cs +++ /dev/null @@ -1,177 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.Serialization; -using System.Threading.Tasks; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Services; - -namespace ServiceStack.Host -{ - public class RestHandler - { - public string RequestName { get; set; } - - public async Task HandleResponseAsync(object response) - { - var taskResponse = response as Task; - - if (taskResponse == null) - { - return response; - } - - await taskResponse.ConfigureAwait(false); - - var taskResult = ServiceStackHost.Instance.GetTaskResult(taskResponse, RequestName); - - var subTask = taskResult as Task; - if (subTask != null) - { - taskResult = ServiceStackHost.Instance.GetTaskResult(subTask, RequestName); - } - - return taskResult; - } - - protected static object CreateContentTypeRequest(IRequest httpReq, Type requestType, string contentType) - { - if (!string.IsNullOrEmpty(contentType) && httpReq.ContentLength > 0) - { - var deserializer = ContentTypes.Instance.GetStreamDeserializer(contentType); - if (deserializer != null) - { - return deserializer(requestType, httpReq.InputStream); - } - } - return ServiceStackHost.Instance.CreateInstance(requestType); //Return an empty DTO, even for empty request bodies - } - - protected static object GetCustomRequestFromBinder(IRequest httpReq, Type requestType) - { - Func requestFactoryFn; - ServiceStackHost.Instance.ServiceController.RequestTypeFactoryMap.TryGetValue( - requestType, out requestFactoryFn); - - return requestFactoryFn != null ? requestFactoryFn(httpReq) : null; - } - - public static RestPath FindMatchingRestPath(string httpMethod, string pathInfo, ILogger logger, out string contentType) - { - pathInfo = GetSanitizedPathInfo(pathInfo, out contentType); - - return ServiceStackHost.Instance.ServiceController.GetRestPathForRequest(httpMethod, pathInfo, logger); - } - - public static string GetSanitizedPathInfo(string pathInfo, out string contentType) - { - contentType = null; - var pos = pathInfo.LastIndexOf('.'); - if (pos >= 0) - { - var format = pathInfo.Substring(pos + 1); - contentType = GetFormatContentType(format); - if (contentType != null) - { - pathInfo = pathInfo.Substring(0, pos); - } - } - return pathInfo; - } - - private static string GetFormatContentType(string format) - { - //built-in formats - if (format == "json") - return "application/json"; - if (format == "xml") - return "application/xml"; - - return null; - } - - public RestPath GetRestPath(string httpMethod, string pathInfo) - { - if (this.RestPath == null) - { - string contentType; - this.RestPath = FindMatchingRestPath(httpMethod, pathInfo, new NullLogger(), out contentType); - - if (contentType != null) - ResponseContentType = contentType; - } - return this.RestPath; - } - - public RestPath RestPath { get; set; } - - // Set from SSHHF.GetHandlerForPathInfo() - public string ResponseContentType { get; set; } - - public async Task ProcessRequestAsync(IRequest httpReq, IResponse httpRes, string operationName) - { - var appHost = ServiceStackHost.Instance; - - var restPath = GetRestPath(httpReq.Verb, httpReq.PathInfo); - if (restPath == null) - { - throw new NotSupportedException("No RestPath found for: " + httpReq.Verb + " " + httpReq.PathInfo); - } - httpReq.SetRoute(restPath); - - if (ResponseContentType != null) - httpReq.ResponseContentType = ResponseContentType; - - var request = httpReq.Dto = CreateRequest(httpReq, restPath); - - appHost.ApplyRequestFilters(httpReq, httpRes, request); - - var rawResponse = await ServiceStackHost.Instance.ServiceController.Execute(request, httpReq).ConfigureAwait(false); - - var response = await HandleResponseAsync(rawResponse).ConfigureAwait(false); - - appHost.ApplyResponseFilters(httpReq, httpRes, response); - - await httpRes.WriteToResponse(httpReq, response).ConfigureAwait(false); - } - - public static object CreateRequest(IRequest httpReq, RestPath restPath) - { - var dtoFromBinder = GetCustomRequestFromBinder(httpReq, restPath.RequestType); - if (dtoFromBinder != null) - return dtoFromBinder; - - var requestParams = httpReq.GetFlattenedRequestParams(); - return CreateRequest(httpReq, restPath, requestParams); - } - - public static object CreateRequest(IRequest httpReq, RestPath restPath, Dictionary requestParams) - { - var requestDto = CreateContentTypeRequest(httpReq, restPath.RequestType, httpReq.ContentType); - - return CreateRequest(httpReq, restPath, requestParams, requestDto); - } - - public static object CreateRequest(IRequest httpReq, RestPath restPath, Dictionary requestParams, object requestDto) - { - string contentType; - var pathInfo = !restPath.IsWildCardPath - ? GetSanitizedPathInfo(httpReq.PathInfo, out contentType) - : httpReq.PathInfo; - - return restPath.CreateRequest(pathInfo, requestParams, requestDto); - } - - /// - /// Used in Unit tests - /// - /// - public object CreateRequest(IRequest httpReq, string operationName) - { - if (this.RestPath == null) - throw new ArgumentNullException("No RestPath found"); - - return CreateRequest(httpReq, this.RestPath); - } - } - -} diff --git a/ServiceStack/Host/ServiceMetadata.cs b/ServiceStack/Host/ServiceMetadata.cs deleted file mode 100644 index 240e6f32d..000000000 --- a/ServiceStack/Host/ServiceMetadata.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace ServiceStack.Host -{ - public class ServiceMetadata - { - public ServiceMetadata() - { - this.OperationsMap = new Dictionary(); - } - - public Dictionary OperationsMap { get; protected set; } - - public void Add(Type serviceType, Type requestType, Type responseType) - { - this.OperationsMap[requestType] = serviceType; - } - - public Type GetServiceTypeByRequest(Type requestType) - { - Type serviceType; - OperationsMap.TryGetValue(requestType, out serviceType); - return serviceType; - } - } -} diff --git a/ServiceStack/HttpHandlerFactory.cs b/ServiceStack/HttpHandlerFactory.cs deleted file mode 100644 index 3a3f5b348..000000000 --- a/ServiceStack/HttpHandlerFactory.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Services; -using ServiceStack.Host; - -namespace ServiceStack -{ - public class HttpHandlerFactory - { - // Entry point for HttpListener - public static RestHandler GetHandler(IHttpRequest httpReq, ILogger logger) - { - var pathInfo = httpReq.PathInfo; - - var pathParts = pathInfo.TrimStart('/').Split('/'); - if (pathParts.Length == 0) - { - logger.Error("Path parts empty for PathInfo: {0}, Url: {1}", pathInfo, httpReq.RawUrl); - return null; - } - - string contentType; - var restPath = RestHandler.FindMatchingRestPath(httpReq.HttpMethod, pathInfo, logger, out contentType); - - if (restPath != null) - { - return new RestHandler - { - RestPath = restPath, - RequestName = restPath.RequestType.GetOperationName(), - ResponseContentType = contentType - }; - } - - logger.Error("Could not find handler for {0}", pathInfo); - return null; - } - } -} \ No newline at end of file diff --git a/ServiceStack/HttpRequestExtensions.cs b/ServiceStack/HttpRequestExtensions.cs deleted file mode 100644 index c34d62601..000000000 --- a/ServiceStack/HttpRequestExtensions.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System; -using System.Collections.Generic; -using MediaBrowser.Model.Services; -using ServiceStack.Host; - -namespace ServiceStack -{ - public static class HttpRequestExtensions - { - /** - * - Input: http://localhost:96/Cambia3/Temp/Test.aspx/path/info?q=item#fragment - - Some HttpRequest path and URL properties: - Request.ApplicationPath: /Cambia3 - Request.CurrentExecutionFilePath: /Cambia3/Temp/Test.aspx - Request.FilePath: /Cambia3/Temp/Test.aspx - Request.Path: /Cambia3/Temp/Test.aspx/path/info - Request.PathInfo: /path/info - Request.PhysicalApplicationPath: D:\Inetpub\wwwroot\CambiaWeb\Cambia3\ - Request.QueryString: /Cambia3/Temp/Test.aspx/path/info?query=arg - Request.Url.AbsolutePath: /Cambia3/Temp/Test.aspx/path/info - Request.Url.AbsoluteUri: http://localhost:96/Cambia3/Temp/Test.aspx/path/info?query=arg - Request.Url.Fragment: - Request.Url.Host: localhost - Request.Url.LocalPath: /Cambia3/Temp/Test.aspx/path/info - Request.Url.PathAndQuery: /Cambia3/Temp/Test.aspx/path/info?query=arg - Request.Url.Port: 96 - Request.Url.Query: ?query=arg - Request.Url.Scheme: http - Request.Url.Segments: / - Cambia3/ - Temp/ - Test.aspx/ - path/ - info - * */ - - /// - /// Duplicate Params are given a unique key by appending a #1 suffix - /// - public static Dictionary GetRequestParams(this IRequest request) - { - var map = new Dictionary(); - - foreach (var name in request.QueryString.Keys) - { - if (name == null) continue; //thank you ASP.NET - - var values = request.QueryString.GetValues(name); - if (values.Length == 1) - { - map[name] = values[0]; - } - else - { - for (var i = 0; i < values.Length; i++) - { - map[name + (i == 0 ? "" : "#" + i)] = values[i]; - } - } - } - - if ((request.Verb == HttpMethods.Post || request.Verb == HttpMethods.Put) - && request.FormData != null) - { - foreach (var name in request.FormData.Keys) - { - if (name == null) continue; //thank you ASP.NET - - var values = request.FormData.GetValues(name); - if (values.Length == 1) - { - map[name] = values[0]; - } - else - { - for (var i = 0; i < values.Length; i++) - { - map[name + (i == 0 ? "" : "#" + i)] = values[i]; - } - } - } - } - - return map; - } - - /// - /// Duplicate params have their values joined together in a comma-delimited string - /// - public static Dictionary GetFlattenedRequestParams(this IRequest request) - { - var map = new Dictionary(); - - foreach (var name in request.QueryString.Keys) - { - if (name == null) continue; //thank you ASP.NET - map[name] = request.QueryString[name]; - } - - if ((request.Verb == HttpMethods.Post || request.Verb == HttpMethods.Put) - && request.FormData != null) - { - foreach (var name in request.FormData.Keys) - { - if (name == null) continue; //thank you ASP.NET - map[name] = request.FormData[name]; - } - } - - return map; - } - - public static void SetRoute(this IRequest req, RestPath route) - { - req.Items["__route"] = route; - } - - public static RestPath GetRoute(this IRequest req) - { - object route; - req.Items.TryGetValue("__route", out route); - return route as RestPath; - } - } -} \ No newline at end of file diff --git a/ServiceStack/HttpUtils.cs b/ServiceStack/HttpUtils.cs deleted file mode 100644 index 41d191d61..000000000 --- a/ServiceStack/HttpUtils.cs +++ /dev/null @@ -1,34 +0,0 @@ -//Copyright (c) Service Stack LLC. All Rights Reserved. -//License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt - -using System; -using System.Collections.Generic; - -namespace ServiceStack -{ - internal static class HttpMethods - { - static readonly string[] allVerbs = new[] { - "OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT", // RFC 2616 - "PROPFIND", "PROPPATCH", "MKCOL", "COPY", "MOVE", "LOCK", "UNLOCK", // RFC 2518 - "VERSION-CONTROL", "REPORT", "CHECKOUT", "CHECKIN", "UNCHECKOUT", - "MKWORKSPACE", "UPDATE", "LABEL", "MERGE", "BASELINE-CONTROL", "MKACTIVITY", // RFC 3253 - "ORDERPATCH", // RFC 3648 - "ACL", // RFC 3744 - "PATCH", // https://datatracker.ietf.org/doc/draft-dusseault-http-patch/ - "SEARCH", // https://datatracker.ietf.org/doc/draft-reschke-webdav-search/ - "BCOPY", "BDELETE", "BMOVE", "BPROPFIND", "BPROPPATCH", "NOTIFY", - "POLL", "SUBSCRIBE", "UNSUBSCRIBE" //MS Exchange WebDav: http://msdn.microsoft.com/en-us/library/aa142917.aspx - }; - - public static HashSet AllVerbs = new HashSet(allVerbs); - - public const string Get = "GET"; - public const string Put = "PUT"; - public const string Post = "POST"; - public const string Delete = "DELETE"; - public const string Options = "OPTIONS"; - public const string Head = "HEAD"; - public const string Patch = "PATCH"; - } -} diff --git a/ServiceStack/ReflectionExtensions.cs b/ServiceStack/ReflectionExtensions.cs index bbabd0dd7..4bbf9e6ac 100644 --- a/ServiceStack/ReflectionExtensions.cs +++ b/ServiceStack/ReflectionExtensions.cs @@ -3,26 +3,11 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; -using System.Runtime.Serialization; -using System.Text; -using System.Threading.Tasks; namespace ServiceStack { public static class ReflectionExtensions { - public static bool IsInstanceOf(this Type type, Type thisOrBaseType) - { - while (type != null) - { - if (type == thisOrBaseType) - return true; - - type = type.BaseType(); - } - return false; - } - public static Type FirstGenericType(this Type type) { while (type != null) @@ -54,44 +39,6 @@ namespace ServiceStack return null; } - public static PropertyInfo[] GetAllProperties(this Type type) - { - if (type.IsInterface()) - { - var propertyInfos = new List(); - - var considered = new List(); - var queue = new Queue(); - considered.Add(type); - queue.Enqueue(type); - - while (queue.Count > 0) - { - var subType = queue.Dequeue(); - foreach (var subInterface in subType.GetTypeInterfaces()) - { - if (considered.Contains(subInterface)) continue; - - considered.Add(subInterface); - queue.Enqueue(subInterface); - } - - var typeProperties = subType.GetTypesProperties(); - - var newPropertyInfos = typeProperties - .Where(x => !propertyInfos.Contains(x)); - - propertyInfos.InsertRange(0, newPropertyInfos); - } - - return propertyInfos.ToArray(); - } - - return type.GetTypesProperties() - .Where(t => t.GetIndexParameters().Length == 0) // ignore indexed properties - .ToArray(); - } - public static PropertyInfo[] GetPublicProperties(this Type type) { if (type.IsInterface()) @@ -139,9 +86,7 @@ namespace ServiceStack public static PropertyInfo[] GetSerializableProperties(this Type type) { - var properties = type.IsDto() - ? type.GetAllProperties() - : type.GetPublicProperties(); + var properties = type.GetPublicProperties(); return properties.OnlySerializableProperties(type); } @@ -150,14 +95,7 @@ namespace ServiceStack public static PropertyInfo[] OnlySerializableProperties(this PropertyInfo[] properties, Type type = null) { - var isDto = type.IsDto(); - var readableProperties = properties.Where(x => x.PropertyGetMethod(nonPublic: isDto) != null); - - if (isDto) - { - return readableProperties.Where(attr => - attr.HasAttribute()).ToArray(); - } + var readableProperties = properties.Where(x => x.PropertyGetMethod(nonPublic: false) != null); // else return those properties that are not decorated with IgnoreDataMember return readableProperties @@ -206,36 +144,6 @@ namespace ServiceStack return pis.ToArray(); } - internal static PropertyInfo[] GetTypesProperties(this Type subType) - { - var pis = new List(); - foreach (var pi in subType.GetRuntimeProperties()) - { - var mi = pi.GetMethod ?? pi.SetMethod; - if (mi != null && mi.IsStatic) continue; - pis.Add(pi); - } - return pis.ToArray(); - } - - public static bool HasAttribute(this Type type) - { - return type.AllAttributes().Any(x => x.GetType() == typeof(T)); - } - - public static bool HasAttribute(this PropertyInfo pi) - { - return pi.AllAttributes().Any(x => x.GetType() == typeof(T)); - } - - public static bool IsDto(this Type type) - { - if (type == null) - return false; - - return type.HasAttribute(); - } - public static MethodInfo PropertyGetMethod(this PropertyInfo pi, bool nonPublic = false) { return pi.GetMethod; @@ -246,25 +154,15 @@ namespace ServiceStack return propertyInfo.GetCustomAttributes(true).ToArray(); } - public static object[] AllAttributes(this PropertyInfo propertyInfo, Type attrType) - { - return propertyInfo.GetCustomAttributes(true).Where(x => attrType.IsInstanceOf(x.GetType())).ToArray(); - } - public static object[] AllAttributes(this Type type) { return type.GetTypeInfo().GetCustomAttributes(true).ToArray(); } - public static TAttr[] AllAttributes(this PropertyInfo pi) - { - return pi.AllAttributes(typeof(TAttr)).Cast().ToArray(); - } - - public static TAttr[] AllAttributes(this Type type) + public static List AllAttributes(this Type type) where TAttr : Attribute { - return type.GetTypeInfo().GetCustomAttributes(true).ToArray(); + return type.GetTypeInfo().GetCustomAttributes(true).ToList(); } } } diff --git a/ServiceStack/Host/RestPath.cs b/ServiceStack/RestPath.cs similarity index 99% rename from ServiceStack/Host/RestPath.cs rename to ServiceStack/RestPath.cs index 3f896beae..5e86001d3 100644 --- a/ServiceStack/Host/RestPath.cs +++ b/ServiceStack/RestPath.cs @@ -1,12 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reflection; using System.Text; using MediaBrowser.Model.Logging; using ServiceStack.Serialization; -namespace ServiceStack.Host +namespace ServiceStack { public class RestPath { @@ -50,7 +49,7 @@ namespace ServiceStack.Host get { return allowsAllVerbs - ? new[] { ActionContext.AnyAction } + ? new[] { "ANY" } : AllowedVerbs.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); } } diff --git a/ServiceStack/ServiceStack.csproj b/ServiceStack/ServiceStack.csproj index 5413d4e55..36c467b8e 100644 --- a/ServiceStack/ServiceStack.csproj +++ b/ServiceStack/ServiceStack.csproj @@ -69,24 +69,11 @@ false - - - - - - - - - - - - - - + diff --git a/ServiceStack/ServiceStackHost.Runtime.cs b/ServiceStack/ServiceStackHost.Runtime.cs deleted file mode 100644 index aaa50633b..000000000 --- a/ServiceStack/ServiceStackHost.Runtime.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) Service Stack LLC. All Rights Reserved. -// License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt - - -using MediaBrowser.Model.Services; -using ServiceStack.Support.WebHost; - -namespace ServiceStack -{ - public abstract partial class ServiceStackHost - { - /// - /// Applies the request filters. Returns whether or not the request has been handled - /// and no more processing should be done. - /// - /// - public virtual void ApplyRequestFilters(IRequest req, IResponse res, object requestDto) - { - //Exec all RequestFilter attributes with Priority < 0 - var attributes = FilterAttributeCache.GetRequestFilterAttributes(requestDto.GetType()); - var i = 0; - for (; i < attributes.Length && attributes[i].Priority < 0; i++) - { - var attribute = attributes[i]; - attribute.RequestFilter(req, res, requestDto); - } - - //Exec global filters - foreach (var requestFilter in GlobalRequestFilters) - { - requestFilter(req, res, requestDto); - } - - //Exec remaining RequestFilter attributes with Priority >= 0 - for (; i < attributes.Length && attributes[i].Priority >= 0; i++) - { - var attribute = attributes[i]; - attribute.RequestFilter(req, res, requestDto); - } - } - - /// - /// Applies the response filters. Returns whether or not the request has been handled - /// and no more processing should be done. - /// - /// - public virtual void ApplyResponseFilters(IRequest req, IResponse res, object response) - { - //Exec global filters - foreach (var responseFilter in GlobalResponseFilters) - { - responseFilter(req, res, response); - } - } - } - -} \ No newline at end of file diff --git a/ServiceStack/ServiceStackHost.cs b/ServiceStack/ServiceStackHost.cs index 8a1db25e4..09fe9a242 100644 --- a/ServiceStack/ServiceStackHost.cs +++ b/ServiceStack/ServiceStackHost.cs @@ -6,71 +6,24 @@ using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; -using MediaBrowser.Model.Logging; using MediaBrowser.Model.Services; -using ServiceStack.Host; namespace ServiceStack { - public abstract partial class ServiceStackHost : IDisposable + public abstract class ServiceStackHost : IDisposable { public static ServiceStackHost Instance { get; protected set; } - protected ServiceStackHost(string serviceName) + protected ServiceStackHost() { - ServiceName = serviceName; - ServiceController = CreateServiceController(); - - RestPaths = new List(); - Metadata = new ServiceMetadata(); - GlobalRequestFilters = new List>(); GlobalResponseFilters = new List>(); } - public abstract void Configure(); - public abstract object CreateInstance(Type type); - protected abstract ServiceController CreateServiceController(); - - public virtual ServiceStackHost Init() - { - Instance = this; - - ServiceController.Init(); - Configure(); - - ServiceController.AfterInit(); - - return this; - } - - public virtual ServiceStackHost Start(string urlBase) - { - throw new NotImplementedException("Start(listeningAtUrlBase) is not supported by this AppHost"); - } - - public string ServiceName { get; set; } - - public ServiceMetadata Metadata { get; set; } - - public ServiceController ServiceController { get; set; } - - public List RestPaths = new List(); - - public List> GlobalRequestFilters { get; set; } - public List> GlobalResponseFilters { get; set; } - public abstract T TryResolve(); - public abstract T Resolve(); - - public virtual MediaBrowser.Model.Services.RouteAttribute[] GetRouteAttributes(Type requestType) - { - return requestType.AllAttributes(); - } - - public abstract object GetTaskResult(Task task, string requestName); + public abstract RouteAttribute[] GetRouteAttributes(Type requestType); public abstract Func GetParseFn(Type propertyType); @@ -85,20 +38,5 @@ namespace ServiceStack Instance = null; } - - protected abstract ILogger Logger - { - get; - } - - public void OnLogError(Type type, string message) - { - Logger.Error(message); - } - - public void OnLogError(Type type, string message, Exception ex) - { - Logger.ErrorException(message, ex); - } } } diff --git a/ServiceStack/StringMapTypeDeserializer.cs b/ServiceStack/StringMapTypeDeserializer.cs index 762e8aaff..8b76c39d0 100644 --- a/ServiceStack/StringMapTypeDeserializer.cs +++ b/ServiceStack/StringMapTypeDeserializer.cs @@ -48,11 +48,6 @@ namespace ServiceStack.Serialization var propertyParseStringFn = GetParseFn(propertyType); var propertySerializer = new PropertySerializerEntry(propertySetFn, propertyParseStringFn) { PropertyType = propertyType }; - var attr = propertyInfo.AllAttributes().FirstOrDefault(); - if (attr != null && attr.Name != null) - { - propertySetterMap[attr.Name] = propertySerializer; - } propertySetterMap[propertyInfo.Name] = propertySerializer; } }