From 0a03d7ad9fe6554b78963445f012464023113614 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 12 Feb 2017 20:07:48 -0500 Subject: localization fixes --- Emby.Server.Implementations/HttpServer/HttpResultFactory.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations/HttpServer/HttpResultFactory.cs') 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); -- cgit v1.2.3 From e1b2b2e77e03c2f858c06fdeabb2da16f719d921 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 12 Feb 2017 21:06:54 -0500 Subject: removed dead code --- .../HttpServer/HttpListenerHost.cs | 40 ++--- .../HttpServer/HttpResultFactory.cs | 2 +- .../Services/RequestHelper.cs | 14 +- .../Services/ResponseHelper.cs | 3 +- .../Services/ServiceController.cs | 42 +++++- .../Services/ServiceHandler.cs | 24 +-- ServiceStack/ReflectionExtensions.cs | 168 --------------------- ServiceStack/RestPath.cs | 108 +++++++++++-- ServiceStack/ServiceStack.csproj | 2 - ServiceStack/ServiceStackHost.cs | 42 ------ ServiceStack/StringMapTypeDeserializer.cs | 13 +- 11 files changed, 182 insertions(+), 276 deletions(-) delete mode 100644 ServiceStack/ReflectionExtensions.cs delete mode 100644 ServiceStack/ServiceStackHost.cs (limited to 'Emby.Server.Implementations/HttpServer/HttpResultFactory.cs') diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 99ec146d7..8ecf4ad4d 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -29,7 +29,7 @@ using SocketHttpListener.Primitives; namespace Emby.Server.Implementations.HttpServer { - public class HttpListenerHost : ServiceStackHost, IHttpServer + public class HttpListenerHost : IHttpServer, IDisposable { private string DefaultRedirectPath { get; set; } @@ -62,7 +62,10 @@ namespace Emby.Server.Implementations.HttpServer private readonly bool _enableDualModeSockets; public List> RequestFilters { get; set; } - private Dictionary ServiceOperationsMap = new Dictionary(); + public List> ResponseFilters { get; set; } + + private readonly Dictionary ServiceOperationsMap = new Dictionary(); + public static HttpListenerHost Instance { get; protected set; } public HttpListenerHost(IServerApplicationHost applicationHost, ILogger logger, @@ -71,6 +74,8 @@ namespace Emby.Server.Implementations.HttpServer 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() { + Instance = this; + _appHost = applicationHost; DefaultRedirectPath = defaultRedirectPath; _networkManager = networkManager; @@ -90,6 +95,7 @@ namespace Emby.Server.Implementations.HttpServer _logger = logger; RequestFilters = new List>(); + ResponseFilters = new List>(); } public string GlobalResponse { get; set; } @@ -112,7 +118,7 @@ namespace Emby.Server.Implementations.HttpServer } } - public override object CreateInstance(Type type) + public object CreateInstance(Type type) { return _appHost.CreateInstance(type); } @@ -168,12 +174,12 @@ namespace Emby.Server.Implementations.HttpServer private IHasRequestFilter[] GetRequestFilterAttributes(Type requestDtoType) { - var attributes = requestDtoType.AllAttributes().OfType().ToList(); + var attributes = requestDtoType.GetTypeInfo().GetCustomAttributes(true).OfType().ToList(); var serviceType = GetServiceTypeByRequest(requestDtoType); if (serviceType != null) { - attributes.AddRange(serviceType.AllAttributes().OfType()); + attributes.AddRange(serviceType.GetTypeInfo().GetCustomAttributes(true).OfType()); } attributes.Sort((x, y) => x.Priority - y.Priority); @@ -611,7 +617,7 @@ namespace Emby.Server.Implementations.HttpServer _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); @@ -658,8 +664,6 @@ namespace Emby.Server.Implementations.HttpServer _logger.Info("Calling ServiceStack AppHost.Init"); - Instance = this; - ServiceController.Init(this); var requestFilters = _appHost.GetExports().ToList(); @@ -668,12 +672,12 @@ namespace Emby.Server.Implementations.HttpServer RequestFilters.Add(filter.Filter); } - GlobalResponseFilters.Add(new ResponseFilter(_logger).FilterResponse); + ResponseFilters.Add(new ResponseFilter(_logger).FilterResponse); } - public override RouteAttribute[] GetRouteAttributes(Type requestType) + public RouteAttribute[] GetRouteAttributes(Type requestType) { - var routes = requestType.AllAttributes(); + var routes = requestType.GetTypeInfo().GetCustomAttributes(true).ToList(); var clone = routes.ToList(); foreach (var route in clone) @@ -703,27 +707,27 @@ namespace Emby.Server.Implementations.HttpServer return routes.ToArray(); } - public override Func GetParseFn(Type propertyType) + public Func GetParseFn(Type propertyType) { return _funcParseFn(propertyType); } - public override void SerializeToJson(object o, Stream stream) + public void SerializeToJson(object o, Stream stream) { _jsonSerializer.SerializeToStream(o, stream); } - public override void SerializeToXml(object o, Stream stream) + public void SerializeToXml(object o, Stream stream) { _xmlSerializer.SerializeToStream(o, stream); } - public override object DeserializeXml(Type type, Stream stream) + public object DeserializeXml(Type type, Stream stream) { return _xmlSerializer.DeserializeFromStream(type, stream); } - public override object DeserializeJson(Type type, Stream stream) + public object DeserializeJson(Type type, Stream stream) { return _jsonSerializer.DeserializeFromStream(stream, type); } @@ -764,7 +768,7 @@ namespace Emby.Server.Implementations.HttpServer { if (_disposed) return; - base.Dispose(); + Dispose(); lock (_disposeLock) { @@ -779,7 +783,7 @@ namespace Emby.Server.Implementations.HttpServer } } - public override void Dispose() + public void Dispose() { Dispose(true); GC.SuppressFinalize(this); diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index 3f756fc7a..20b345fa1 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -204,7 +204,7 @@ namespace Emby.Server.Implementations.HttpServer using (var ms = new MemoryStream()) { var contentType = request.ResponseContentType; - var writerFn = RequestHelper.GetResponseWriter(contentType); + var writerFn = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType); writerFn(dto, ms); diff --git a/Emby.Server.Implementations/Services/RequestHelper.cs b/Emby.Server.Implementations/Services/RequestHelper.cs index 8cfc3d089..7538d3102 100644 --- a/Emby.Server.Implementations/Services/RequestHelper.cs +++ b/Emby.Server.Implementations/Services/RequestHelper.cs @@ -1,40 +1,40 @@ using System; using System.IO; -using ServiceStack; +using Emby.Server.Implementations.HttpServer; namespace Emby.Server.Implementations.Services { public class RequestHelper { - public static Func GetRequestReader(string contentType) + public static Func GetRequestReader(HttpListenerHost host, 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 ServiceStackHost.Instance.DeserializeXml; + return host.DeserializeXml; case "application/json": case "text/json": - return ServiceStackHost.Instance.DeserializeJson; + return host.DeserializeJson; } return null; } - public static Action GetResponseWriter(string contentType) + public static Action GetResponseWriter(HttpListenerHost host, 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); + return host.SerializeToXml; case "application/json": case "text/json": - return (o, s) => ServiceStackHost.Instance.SerializeToJson(o, s); + return host.SerializeToJson; } return null; diff --git a/Emby.Server.Implementations/Services/ResponseHelper.cs b/Emby.Server.Implementations/Services/ResponseHelper.cs index 1af70ad7f..d4ce1cabf 100644 --- a/Emby.Server.Implementations/Services/ResponseHelper.cs +++ b/Emby.Server.Implementations/Services/ResponseHelper.cs @@ -5,6 +5,7 @@ using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; +using Emby.Server.Implementations.HttpServer; using MediaBrowser.Model.Services; namespace Emby.Server.Implementations.Services @@ -161,7 +162,7 @@ namespace Emby.Server.Implementations.Services public static async Task WriteObject(IRequest request, object result, IResponse response) { var contentType = request.ResponseContentType; - var serializer = RequestHelper.GetResponseWriter(contentType); + var serializer = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType); using (var ms = new MemoryStream()) { diff --git a/Emby.Server.Implementations/Services/ServiceController.cs b/Emby.Server.Implementations/Services/ServiceController.cs index 714a16df5..da8af89c8 100644 --- a/Emby.Server.Implementations/Services/ServiceController.cs +++ b/Emby.Server.Implementations/Services/ServiceController.cs @@ -53,28 +53,58 @@ namespace Emby.Server.Implementations.Services ServiceExecGeneral.CreateServiceRunnersFor(requestType, actions); - var returnMarker = requestType.GetTypeWithGenericTypeDefinitionOf(typeof(IReturn<>)); + 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(requestType); + RegisterRestPaths(appHost, requestType); appHost.AddServiceInfo(serviceType, requestType, responseType); } } + private static Type GetTypeWithGenericTypeDefinitionOf(Type type, Type genericTypeDefinition) + { + foreach (var t in type.GetTypeInfo().ImplementedInterfaces) + { + if (t.GetTypeInfo().IsGenericType && t.GetGenericTypeDefinition() == genericTypeDefinition) + { + return t; + } + } + + var genericType = FirstGenericType(type); + if (genericType != null && genericType.GetGenericTypeDefinition() == genericTypeDefinition) + { + return genericType; + } + + return null; + } + + public static Type FirstGenericType(Type type) + { + while (type != null) + { + if (type.GetTypeInfo().IsGenericType) + return type; + + type = type.GetTypeInfo().BaseType; + } + return null; + } + public readonly Dictionary> RestPathMap = new Dictionary>(StringComparer.OrdinalIgnoreCase); - public void RegisterRestPaths(Type requestType) + public void RegisterRestPaths(HttpListenerHost appHost, Type requestType) { - var appHost = ServiceStackHost.Instance; var attrs = appHost.GetRouteAttributes(requestType); - foreach (MediaBrowser.Model.Services.RouteAttribute attr in attrs) + foreach (RouteAttribute attr in attrs) { - var restPath = new RestPath(requestType, attr.Path, attr.Verbs, attr.Summary, attr.Notes); + var restPath = new RestPath(appHost.CreateInstance, appHost.GetParseFn, requestType, attr.Path, attr.Verbs, attr.Summary, attr.Notes); if (!restPath.IsValid) throw new NotSupportedException(string.Format( diff --git a/Emby.Server.Implementations/Services/ServiceHandler.cs b/Emby.Server.Implementations/Services/ServiceHandler.cs index 003776f9c..93126426c 100644 --- a/Emby.Server.Implementations/Services/ServiceHandler.cs +++ b/Emby.Server.Implementations/Services/ServiceHandler.cs @@ -59,17 +59,17 @@ namespace Emby.Server.Implementations.Services } } - protected static object CreateContentTypeRequest(IRequest httpReq, Type requestType, string contentType) + protected static object CreateContentTypeRequest(HttpListenerHost host, IRequest httpReq, Type requestType, string contentType) { if (!string.IsNullOrEmpty(contentType) && httpReq.ContentLength > 0) { - var deserializer = RequestHelper.GetRequestReader(contentType); + var deserializer = RequestHelper.GetRequestReader(host, contentType); if (deserializer != null) { return deserializer(requestType, httpReq.InputStream); } } - return ServiceStackHost.Instance.CreateInstance(requestType); //Return an empty DTO, even for empty request bodies + return host.CreateInstance(requestType); } public static RestPath FindMatchingRestPath(string httpMethod, string pathInfo, ILogger logger, out string contentType) @@ -137,7 +137,7 @@ namespace Emby.Server.Implementations.Services if (ResponseContentType != null) httpReq.ResponseContentType = ResponseContentType; - var request = httpReq.Dto = CreateRequest(httpReq, restPath, logger); + var request = httpReq.Dto = CreateRequest(appHost, httpReq, restPath, logger); appHost.ApplyRequestFilters(httpReq, httpRes, request); @@ -146,7 +146,7 @@ namespace Emby.Server.Implementations.Services var response = await HandleResponseAsync(rawResponse).ConfigureAwait(false); // Apply response filters - foreach (var responseFilter in appHost.GlobalResponseFilters) + foreach (var responseFilter in appHost.ResponseFilters) { responseFilter(httpReq, httpRes, response); } @@ -154,18 +154,18 @@ namespace Emby.Server.Implementations.Services await ResponseHelper.WriteToResponse(httpRes, httpReq, response).ConfigureAwait(false); } - public static object CreateRequest(IRequest httpReq, RestPath restPath, ILogger logger) + public static object CreateRequest(HttpListenerHost host, IRequest httpReq, RestPath restPath, ILogger logger) { var requestType = restPath.RequestType; if (RequireqRequestStream(requestType)) { // Used by IRequiresRequestStream - return CreateRequiresRequestStreamRequest(httpReq, requestType); + return CreateRequiresRequestStreamRequest(host, httpReq, requestType); } var requestParams = GetFlattenedRequestParams(httpReq); - return CreateRequest(httpReq, restPath, requestParams); + return CreateRequest(host, httpReq, restPath, requestParams); } private static bool RequireqRequestStream(Type requestType) @@ -175,19 +175,19 @@ namespace Emby.Server.Implementations.Services return requiresRequestStreamTypeInfo.IsAssignableFrom(requestType.GetTypeInfo()); } - private static IRequiresRequestStream CreateRequiresRequestStreamRequest(IRequest req, Type requestType) + private static IRequiresRequestStream CreateRequiresRequestStreamRequest(HttpListenerHost host, IRequest req, Type requestType) { var restPath = GetRoute(req); - var request = ServiceHandler.CreateRequest(req, restPath, GetRequestParams(req), ServiceStackHost.Instance.CreateInstance(requestType)); + var request = ServiceHandler.CreateRequest(req, restPath, GetRequestParams(req), host.CreateInstance(requestType)); var rawReq = (IRequiresRequestStream)request; rawReq.RequestStream = req.InputStream; return rawReq; } - public static object CreateRequest(IRequest httpReq, RestPath restPath, Dictionary requestParams) + public static object CreateRequest(HttpListenerHost host, IRequest httpReq, RestPath restPath, Dictionary requestParams) { - var requestDto = CreateContentTypeRequest(httpReq, restPath.RequestType, httpReq.ContentType); + var requestDto = CreateContentTypeRequest(host, httpReq, restPath.RequestType, httpReq.ContentType); return CreateRequest(httpReq, restPath, requestParams, requestDto); } diff --git a/ServiceStack/ReflectionExtensions.cs b/ServiceStack/ReflectionExtensions.cs deleted file mode 100644 index 4bbf9e6ac..000000000 --- a/ServiceStack/ReflectionExtensions.cs +++ /dev/null @@ -1,168 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; - -namespace ServiceStack -{ - public static class ReflectionExtensions - { - public static Type FirstGenericType(this Type type) - { - while (type != null) - { - if (type.IsGeneric()) - return type; - - type = type.BaseType(); - } - return null; - } - - public static Type GetTypeWithGenericTypeDefinitionOf(this Type type, Type genericTypeDefinition) - { - foreach (var t in type.GetTypeInterfaces()) - { - if (t.IsGeneric() && t.GetGenericTypeDefinition() == genericTypeDefinition) - { - return t; - } - } - - var genericType = type.FirstGenericType(); - if (genericType != null && genericType.GetGenericTypeDefinition() == genericTypeDefinition) - { - return genericType; - } - - return null; - } - - public static PropertyInfo[] GetPublicProperties(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.GetTypesPublicProperties(); - - var newPropertyInfos = typeProperties - .Where(x => !propertyInfos.Contains(x)); - - propertyInfos.InsertRange(0, newPropertyInfos); - } - - return propertyInfos.ToArray(); - } - - return type.GetTypesPublicProperties() - .Where(t => t.GetIndexParameters().Length == 0) // ignore indexed properties - .ToArray(); - } - - public const string DataMember = "DataMemberAttribute"; - - internal static string[] IgnoreAttributesNamed = new[] { - "IgnoreDataMemberAttribute", - "JsonIgnoreAttribute" - }; - - public static PropertyInfo[] GetSerializableProperties(this Type type) - { - var properties = type.GetPublicProperties(); - return properties.OnlySerializableProperties(type); - } - - - private static List _excludeTypes = new List { typeof(Stream) }; - - public static PropertyInfo[] OnlySerializableProperties(this PropertyInfo[] properties, Type type = null) - { - var readableProperties = properties.Where(x => x.PropertyGetMethod(nonPublic: false) != null); - - // else return those properties that are not decorated with IgnoreDataMember - return readableProperties - .Where(prop => prop.AllAttributes() - .All(attr => - { - var name = attr.GetType().Name; - return !IgnoreAttributesNamed.Contains(name); - })) - .Where(prop => !_excludeTypes.Contains(prop.PropertyType)) - .ToArray(); - } - } - - public static class PlatformExtensions //Because WinRT is a POS - { - public static bool IsInterface(this Type type) - { - return type.GetTypeInfo().IsInterface; - } - - public static bool IsGeneric(this Type type) - { - return type.GetTypeInfo().IsGenericType; - } - - public static Type BaseType(this Type type) - { - return type.GetTypeInfo().BaseType; - } - - public static Type[] GetTypeInterfaces(this Type type) - { - return type.GetTypeInfo().ImplementedInterfaces.ToArray(); - } - - internal static PropertyInfo[] GetTypesPublicProperties(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 MethodInfo PropertyGetMethod(this PropertyInfo pi, bool nonPublic = false) - { - return pi.GetMethod; - } - - public static object[] AllAttributes(this PropertyInfo propertyInfo) - { - return propertyInfo.GetCustomAttributes(true).ToArray(); - } - - public static object[] AllAttributes(this Type type) - { - return type.GetTypeInfo().GetCustomAttributes(true).ToArray(); - } - - public static List AllAttributes(this Type type) - where TAttr : Attribute - { - return type.GetTypeInfo().GetCustomAttributes(true).ToList(); - } - } -} diff --git a/ServiceStack/RestPath.cs b/ServiceStack/RestPath.cs index 5e86001d3..afd1f73e1 100644 --- a/ServiceStack/RestPath.cs +++ b/ServiceStack/RestPath.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Reflection; using System.Text; using MediaBrowser.Model.Logging; using ServiceStack.Serialization; @@ -71,7 +73,7 @@ namespace ServiceStack public static string[] GetPathPartsForMatching(string pathInfo) { var parts = pathInfo.ToLower().Split(PathSeperatorChar) - .Where(x => !string.IsNullOrEmpty(x)).ToArray(); + .Where(x => !String.IsNullOrEmpty(x)).ToArray(); return parts; } @@ -107,14 +109,14 @@ namespace ServiceStack return list; } - public RestPath(Type requestType, string path, string verbs, string summary = null, string notes = null) + public RestPath(Func createInstanceFn, Func> getParseFn, Type requestType, string path, string verbs, string summary = null, string notes = null) { this.RequestType = requestType; this.Summary = summary; this.Notes = notes; this.restPath = path; - this.allowsAllVerbs = verbs == null || string.Equals(verbs, WildCard, StringComparison.OrdinalIgnoreCase); + this.allowsAllVerbs = verbs == null || String.Equals(verbs, WildCard, StringComparison.OrdinalIgnoreCase); if (!this.allowsAllVerbs) { this.allowedVerbs = verbs.ToUpper(); @@ -126,7 +128,7 @@ namespace ServiceStack var hasSeparators = new List(); foreach (var component in this.restPath.Split(PathSeperatorChar)) { - if (string.IsNullOrEmpty(component)) continue; + if (String.IsNullOrEmpty(component)) continue; if (StringContains(component, VariablePrefix) && component.IndexOf(ComponentSeperator) != -1) @@ -199,19 +201,95 @@ namespace ServiceStack this.IsValid = sbHashKey.Length > 0; this.UniqueMatchHashKey = sbHashKey.ToString(); - this.typeDeserializer = new StringMapTypeDeserializer(this.RequestType); + this.typeDeserializer = new StringMapTypeDeserializer(createInstanceFn, getParseFn, this.RequestType); RegisterCaseInsenstivePropertyNameMappings(); } private void RegisterCaseInsenstivePropertyNameMappings() { - foreach (var propertyInfo in RequestType.GetSerializableProperties()) + foreach (var propertyInfo in GetSerializableProperties(RequestType)) { var propertyName = propertyInfo.Name; propertyNamesMap.Add(propertyName.ToLower(), propertyName); } } + internal static string[] IgnoreAttributesNamed = new[] { + "IgnoreDataMemberAttribute", + "JsonIgnoreAttribute" + }; + + + private static List _excludeTypes = new List { typeof(Stream) }; + + internal static PropertyInfo[] GetSerializableProperties(Type type) + { + var properties = GetPublicProperties(type); + var readableProperties = properties.Where(x => x.GetMethod != null); + + // else return those properties that are not decorated with IgnoreDataMember + return readableProperties + .Where(prop => prop.GetCustomAttributes(true) + .All(attr => + { + var name = attr.GetType().Name; + return !IgnoreAttributesNamed.Contains(name); + })) + .Where(prop => !_excludeTypes.Contains(prop.PropertyType)) + .ToArray(); + } + + private static PropertyInfo[] GetPublicProperties(Type type) + { + if (type.GetTypeInfo().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.GetTypeInfo().ImplementedInterfaces) + { + if (considered.Contains(subInterface)) continue; + + considered.Add(subInterface); + queue.Enqueue(subInterface); + } + + var typeProperties = GetTypesPublicProperties(subType); + + var newPropertyInfos = typeProperties + .Where(x => !propertyInfos.Contains(x)); + + propertyInfos.InsertRange(0, newPropertyInfos); + } + + return propertyInfos.ToArray(); + } + + return GetTypesPublicProperties(type) + .Where(t => t.GetIndexParameters().Length == 0) // ignore indexed properties + .ToArray(); + } + + private static PropertyInfo[] GetTypesPublicProperties(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 bool IsValid { get; set; } /// @@ -243,7 +321,7 @@ namespace ServiceStack score += Math.Max((10 - VariableArgsCount), 1) * 100; //Exact verb match is better than ANY - var exactVerb = string.Equals(httpMethod, AllowedVerbs, StringComparison.OrdinalIgnoreCase); + var exactVerb = String.Equals(httpMethod, AllowedVerbs, StringComparison.OrdinalIgnoreCase); score += exactVerb ? 10 : 1; return score; @@ -339,7 +417,7 @@ namespace ServiceStack private bool LiteralsEqual(string str1, string str2) { // Most cases - if (string.Equals(str1, str2, StringComparison.OrdinalIgnoreCase)) + if (String.Equals(str1, str2, StringComparison.OrdinalIgnoreCase)) { return true; } @@ -349,7 +427,7 @@ namespace ServiceStack str2 = str2.ToUpperInvariant(); // Invariant IgnoreCase would probably be better but it's not available in PCL - return string.Equals(str1, str2, StringComparison.CurrentCultureIgnoreCase); + return String.Equals(str1, str2, StringComparison.CurrentCultureIgnoreCase); } private bool ExplodeComponents(ref string[] withPathInfoParts) @@ -358,7 +436,7 @@ namespace ServiceStack for (var i = 0; i < withPathInfoParts.Length; i++) { var component = withPathInfoParts[i]; - if (string.IsNullOrEmpty(component)) continue; + if (String.IsNullOrEmpty(component)) continue; if (this.PathComponentsCount != this.TotalComponentsCount && this.componentsWithSeparators[i]) @@ -380,7 +458,7 @@ namespace ServiceStack public object CreateRequest(string pathInfo, Dictionary queryStringAndFormData, object fromInstance) { var requestComponents = pathInfo.Split(PathSeperatorChar) - .Where(x => !string.IsNullOrEmpty(x)).ToArray(); + .Where(x => !String.IsNullOrEmpty(x)).ToArray(); ExplodeComponents(ref requestComponents); @@ -390,7 +468,7 @@ namespace ServiceStack && requestComponents.Length >= this.TotalComponentsCount - this.wildcardCount; if (!isValidWildCardPath) - throw new ArgumentException(string.Format( + throw new ArgumentException(String.Format( "Path Mismatch: Request Path '{0}' has invalid number of components compared to: '{1}'", pathInfo, this.restPath)); } @@ -409,7 +487,7 @@ namespace ServiceStack string propertyNameOnRequest; if (!this.propertyNamesMap.TryGetValue(variableName.ToLower(), out propertyNameOnRequest)) { - if (string.Equals("ignore", variableName, StringComparison.OrdinalIgnoreCase)) + if (String.Equals("ignore", variableName, StringComparison.OrdinalIgnoreCase)) { pathIx++; continue; @@ -439,12 +517,12 @@ namespace ServiceStack // hits a match for the next element in the definition (which must be a literal) // It may consume 0 or more path parts var stopLiteral = i == this.TotalComponentsCount - 1 ? null : this.literalsToMatch[i + 1]; - if (!string.Equals(requestComponents[pathIx], stopLiteral, StringComparison.OrdinalIgnoreCase)) + if (!String.Equals(requestComponents[pathIx], stopLiteral, StringComparison.OrdinalIgnoreCase)) { var sb = new StringBuilder(); sb.Append(value); pathIx++; - while (!string.Equals(requestComponents[pathIx], stopLiteral, StringComparison.OrdinalIgnoreCase)) + while (!String.Equals(requestComponents[pathIx], stopLiteral, StringComparison.OrdinalIgnoreCase)) { sb.Append(PathSeperatorChar + requestComponents[pathIx++]); } diff --git a/ServiceStack/ServiceStack.csproj b/ServiceStack/ServiceStack.csproj index 36c467b8e..33226971e 100644 --- a/ServiceStack/ServiceStack.csproj +++ b/ServiceStack/ServiceStack.csproj @@ -69,9 +69,7 @@ false - - diff --git a/ServiceStack/ServiceStackHost.cs b/ServiceStack/ServiceStackHost.cs deleted file mode 100644 index 09fe9a242..000000000 --- a/ServiceStack/ServiceStackHost.cs +++ /dev/null @@ -1,42 +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; -using System.IO; -using System.Threading.Tasks; -using MediaBrowser.Model.Services; - -namespace ServiceStack -{ - public abstract class ServiceStackHost : IDisposable - { - public static ServiceStackHost Instance { get; protected set; } - - protected ServiceStackHost() - { - GlobalResponseFilters = new List>(); - } - - public abstract object CreateInstance(Type type); - - public List> GlobalResponseFilters { get; set; } - - public abstract RouteAttribute[] GetRouteAttributes(Type requestType); - - public abstract Func GetParseFn(Type propertyType); - - public abstract void SerializeToJson(object o, Stream stream); - public abstract void SerializeToXml(object o, Stream stream); - public abstract object DeserializeXml(Type type, Stream stream); - public abstract object DeserializeJson(Type type, Stream stream); - - public virtual void Dispose() - { - //JsConfig.Reset(); //Clears Runtime Attributes - - Instance = null; - } - } -} diff --git a/ServiceStack/StringMapTypeDeserializer.cs b/ServiceStack/StringMapTypeDeserializer.cs index 8b76c39d0..82724fc4a 100644 --- a/ServiceStack/StringMapTypeDeserializer.cs +++ b/ServiceStack/StringMapTypeDeserializer.cs @@ -34,14 +34,19 @@ namespace ServiceStack.Serialization if (propertyType == typeof(string)) return s => s; - return ServiceStackHost.Instance.GetParseFn(propertyType); + return _GetParseFn(propertyType); } - public StringMapTypeDeserializer(Type type) + private readonly Func _CreateInstanceFn; + private readonly Func> _GetParseFn; + + public StringMapTypeDeserializer(Func createInstanceFn, Func> getParseFn, Type type) { + _CreateInstanceFn = createInstanceFn; + _GetParseFn = getParseFn; this.type = type; - foreach (var propertyInfo in type.GetSerializableProperties()) + foreach (var propertyInfo in RestPath.GetSerializableProperties(type)) { var propertySetFn = TypeAccessor.GetSetPropertyMethod(type, propertyInfo); var propertyType = propertyInfo.PropertyType; @@ -59,7 +64,7 @@ namespace ServiceStack.Serialization PropertySerializerEntry propertySerializerEntry = null; if (instance == null) - instance = ServiceStackHost.Instance.CreateInstance(type); + instance = _CreateInstanceFn(type); foreach (var pair in keyValuePairs.Where(x => !string.IsNullOrEmpty(x.Value))) { -- cgit v1.2.3 From 5181b31886f5f4cc31890bbe4810dd467996e903 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 13 Feb 2017 15:54:28 -0500 Subject: implement chrome media session api --- .../Emby.Server.Implementations.csproj | 7 +- .../HttpServer/HttpListenerHost.cs | 1 - .../HttpServer/HttpResultFactory.cs | 1 - Emby.Server.Implementations/Services/HttpResult.cs | 1 - .../Services/ServiceController.cs | 9 +- .../Services/ServiceExec.cs | 5 +- .../Services/ServiceHandler.cs | 1 - .../Services/ServicePath.cs | 563 ++++++++++++++++++++ .../Services/StringMapTypeDeserializer.cs | 124 +++++ .../Services/UrlExtensions.cs | 33 ++ MediaBrowser.Mono.sln | 18 - .../Music/MusicBrainzAlbumProvider.cs | 55 +- .../MediaBrowser.Server.Mono.csproj | 4 - .../MediaBrowser.ServerApplication.csproj | 4 - MediaBrowser.sln | 42 -- ServiceStack/Properties/AssemblyInfo.cs | 25 - ServiceStack/RestPath.cs | 564 --------------------- ServiceStack/ServiceStack.csproj | 115 ----- ServiceStack/ServiceStack.nuget.targets | 6 - ServiceStack/StringMapTypeDeserializer.cs | 126 ----- ServiceStack/UrlExtensions.cs | 33 -- ServiceStack/packages.config | 3 - ServiceStack/project.json | 17 - 23 files changed, 777 insertions(+), 980 deletions(-) create mode 100644 Emby.Server.Implementations/Services/ServicePath.cs create mode 100644 Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs create mode 100644 Emby.Server.Implementations/Services/UrlExtensions.cs delete mode 100644 ServiceStack/Properties/AssemblyInfo.cs delete mode 100644 ServiceStack/RestPath.cs delete mode 100644 ServiceStack/ServiceStack.csproj delete mode 100644 ServiceStack/ServiceStack.nuget.targets delete mode 100644 ServiceStack/StringMapTypeDeserializer.cs delete mode 100644 ServiceStack/UrlExtensions.cs delete mode 100644 ServiceStack/packages.config delete mode 100644 ServiceStack/project.json (limited to 'Emby.Server.Implementations/HttpServer/HttpResultFactory.cs') diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index b1601df05..fec0c2294 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -215,6 +215,7 @@ + @@ -222,6 +223,8 @@ + + @@ -308,10 +311,6 @@ {2e781478-814d-4a48-9d80-bff206441a65} MediaBrowser.Server.Implementations - - {680a1709-25eb-4d52-a87f-ee03ffd94baa} - ServiceStack - {4f26d5d8-a7b0-42b3-ba42-7cb7d245934e} SocketHttpListener.Portable diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 8ecf4ad4d..c65289e13 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -2,7 +2,6 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Logging; -using ServiceStack; using System; using System.Collections.Generic; using System.IO; diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index 20b345fa1..6bfd83110 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -16,7 +16,6 @@ using Emby.Server.Implementations.HttpServer; using Emby.Server.Implementations.Services; using MediaBrowser.Model.IO; using MediaBrowser.Model.Services; -using ServiceStack; using IRequest = MediaBrowser.Model.Services.IRequest; using MimeTypes = MediaBrowser.Model.Net.MimeTypes; using StreamWriter = Emby.Server.Implementations.HttpServer.StreamWriter; diff --git a/Emby.Server.Implementations/Services/HttpResult.cs b/Emby.Server.Implementations/Services/HttpResult.cs index 585c3e4f8..dfad09f7b 100644 --- a/Emby.Server.Implementations/Services/HttpResult.cs +++ b/Emby.Server.Implementations/Services/HttpResult.cs @@ -4,7 +4,6 @@ using System.Net; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.Services; -using ServiceStack; namespace Emby.Server.Implementations.Services { diff --git a/Emby.Server.Implementations/Services/ServiceController.cs b/Emby.Server.Implementations/Services/ServiceController.cs index da8af89c8..d283bf81f 100644 --- a/Emby.Server.Implementations/Services/ServiceController.cs +++ b/Emby.Server.Implementations/Services/ServiceController.cs @@ -5,7 +5,6 @@ using System.Threading.Tasks; using Emby.Server.Implementations.HttpServer; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Services; -using ServiceStack; namespace Emby.Server.Implementations.Services { @@ -108,7 +107,7 @@ namespace Emby.Server.Implementations.Services if (!restPath.IsValid) throw new NotSupportedException(string.Format( - "RestPath '{0}' on Type '{1}' is not Valid", attr.Path, requestType.GetOperationName())); + "RestPath '{0}' on Type '{1}' is not Valid", attr.Path, requestType.GetMethodName())); RegisterRestPath(restPath); } @@ -119,10 +118,10 @@ namespace Emby.Server.Implementations.Services public void RegisterRestPath(RestPath restPath) { if (!restPath.Path.StartsWith("/")) - throw new ArgumentException(string.Format("Route '{0}' on '{1}' must start with a '/'", restPath.Path, restPath.RequestType.GetOperationName())); + throw new ArgumentException(string.Format("Route '{0}' on '{1}' must start with a '/'", restPath.Path, restPath.RequestType.GetMethodName())); if (restPath.Path.IndexOfAny(InvalidRouteChars) != -1) throw new ArgumentException(string.Format("Route '{0}' on '{1}' contains invalid chars. " + - "See https://github.com/ServiceStack/ServiceStack/wiki/Routing for info on valid routes.", restPath.Path, restPath.RequestType.GetOperationName())); + "See https://github.com/ServiceStack/ServiceStack/wiki/Routing for info on valid routes.", restPath.Path, restPath.RequestType.GetMethodName())); List pathsAtFirstMatch; if (!RestPathMap.TryGetValue(restPath.FirstMatchHashKey, out pathsAtFirstMatch)) @@ -210,7 +209,7 @@ namespace Emby.Server.Implementations.Services req.Dto = requestDto; //Executes the service and returns the result - var response = await ServiceExecGeneral.Execute(serviceType, req, service, requestDto, requestType.GetOperationName()).ConfigureAwait(false); + var response = await ServiceExecGeneral.Execute(serviceType, req, service, requestDto, requestType.GetMethodName()).ConfigureAwait(false); return response; } diff --git a/Emby.Server.Implementations/Services/ServiceExec.cs b/Emby.Server.Implementations/Services/ServiceExec.cs index 59af3078f..e0b5e69c0 100644 --- a/Emby.Server.Implementations/Services/ServiceExec.cs +++ b/Emby.Server.Implementations/Services/ServiceExec.cs @@ -5,7 +5,6 @@ using System.Linq.Expressions; using System.Reflection; using System.Threading.Tasks; using MediaBrowser.Model.Services; -using ServiceStack; namespace Emby.Server.Implementations.Services { @@ -84,7 +83,7 @@ namespace Emby.Server.Implementations.Services } var expectedMethodName = actionName.Substring(0, 1) + actionName.Substring(1).ToLower(); - throw new NotImplementedException(string.Format("Could not find method named {1}({0}) or Any({0}) on Service {2}", requestDto.GetType().GetOperationName(), expectedMethodName, serviceType.GetOperationName())); + throw new NotImplementedException(string.Format("Could not find method named {1}({0}) or Any({0}) on Service {2}", requestDto.GetType().GetMethodName(), expectedMethodName, serviceType.GetMethodName())); } public static List Reset(Type serviceType) @@ -99,7 +98,7 @@ namespace Emby.Server.Implementations.Services var requestType = args[0].ParameterType; var actionCtx = new ServiceMethod { - Id = ServiceMethod.Key(serviceType, actionName, requestType.GetOperationName()) + Id = ServiceMethod.Key(serviceType, actionName, requestType.GetMethodName()) }; try diff --git a/Emby.Server.Implementations/Services/ServiceHandler.cs b/Emby.Server.Implementations/Services/ServiceHandler.cs index 93126426c..8b59b4843 100644 --- a/Emby.Server.Implementations/Services/ServiceHandler.cs +++ b/Emby.Server.Implementations/Services/ServiceHandler.cs @@ -5,7 +5,6 @@ using System.Threading.Tasks; using Emby.Server.Implementations.HttpServer; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Services; -using ServiceStack; namespace Emby.Server.Implementations.Services { diff --git a/Emby.Server.Implementations/Services/ServicePath.cs b/Emby.Server.Implementations/Services/ServicePath.cs new file mode 100644 index 000000000..255b20919 --- /dev/null +++ b/Emby.Server.Implementations/Services/ServicePath.cs @@ -0,0 +1,563 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using MediaBrowser.Model.Logging; + +namespace Emby.Server.Implementations.Services +{ + public class RestPath + { + private const string WildCard = "*"; + private const char WildCardChar = '*'; + private const string PathSeperator = "/"; + private const char PathSeperatorChar = '/'; + private const char ComponentSeperator = '.'; + private const string VariablePrefix = "{"; + + readonly bool[] componentsWithSeparators; + + private readonly string restPath; + private readonly string allowedVerbs; + private readonly bool allowsAllVerbs; + public bool IsWildCardPath { get; private set; } + + private readonly string[] literalsToMatch; + + private readonly string[] variablesNames; + + private readonly bool[] isWildcard; + private readonly int wildcardCount = 0; + + public int VariableArgsCount { get; set; } + + /// + /// The number of segments separated by '/' determinable by path.Split('/').Length + /// e.g. /path/to/here.ext == 3 + /// + public int PathComponentsCount { get; set; } + + /// + /// The total number of segments after subparts have been exploded ('.') + /// e.g. /path/to/here.ext == 4 + /// + public int TotalComponentsCount { get; set; } + + public string[] Verbs + { + get + { + return allowsAllVerbs + ? new[] { "ANY" } + : AllowedVerbs.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); + } + } + + public Type RequestType { get; private set; } + + public string Path { get { return this.restPath; } } + + public string Summary { get; private set; } + + public string Notes { get; private set; } + + public bool AllowsAllVerbs { get { return this.allowsAllVerbs; } } + + public string AllowedVerbs { get { return this.allowedVerbs; } } + + public int Priority { get; set; } //passed back to RouteAttribute + + public static string[] GetPathPartsForMatching(string pathInfo) + { + var parts = pathInfo.ToLower().Split(PathSeperatorChar) + .Where(x => !String.IsNullOrEmpty(x)).ToArray(); + return parts; + } + + public static List GetFirstMatchHashKeys(string[] pathPartsForMatching) + { + var hashPrefix = pathPartsForMatching.Length + PathSeperator; + return GetPotentialMatchesWithPrefix(hashPrefix, pathPartsForMatching); + } + + public static List GetFirstMatchWildCardHashKeys(string[] pathPartsForMatching) + { + const string hashPrefix = WildCard + PathSeperator; + return GetPotentialMatchesWithPrefix(hashPrefix, pathPartsForMatching); + } + + private static List GetPotentialMatchesWithPrefix(string hashPrefix, string[] pathPartsForMatching) + { + var list = new List(); + + foreach (var part in pathPartsForMatching) + { + list.Add(hashPrefix + part); + + var subParts = part.Split(ComponentSeperator); + if (subParts.Length == 1) continue; + + foreach (var subPart in subParts) + { + list.Add(hashPrefix + subPart); + } + } + + return list; + } + + public RestPath(Func createInstanceFn, Func> getParseFn, Type requestType, string path, string verbs, string summary = null, string notes = null) + { + this.RequestType = requestType; + this.Summary = summary; + this.Notes = notes; + this.restPath = path; + + this.allowsAllVerbs = verbs == null || String.Equals(verbs, WildCard, StringComparison.OrdinalIgnoreCase); + if (!this.allowsAllVerbs) + { + this.allowedVerbs = verbs.ToUpper(); + } + + var componentsList = new List(); + + //We only split on '.' if the restPath has them. Allows for /{action}.{type} + var hasSeparators = new List(); + foreach (var component in this.restPath.Split(PathSeperatorChar)) + { + if (String.IsNullOrEmpty(component)) continue; + + if (StringContains(component, VariablePrefix) + && component.IndexOf(ComponentSeperator) != -1) + { + hasSeparators.Add(true); + componentsList.AddRange(component.Split(ComponentSeperator)); + } + else + { + hasSeparators.Add(false); + componentsList.Add(component); + } + } + + var components = componentsList.ToArray(); + this.TotalComponentsCount = components.Length; + + this.literalsToMatch = new string[this.TotalComponentsCount]; + this.variablesNames = new string[this.TotalComponentsCount]; + this.isWildcard = new bool[this.TotalComponentsCount]; + this.componentsWithSeparators = hasSeparators.ToArray(); + this.PathComponentsCount = this.componentsWithSeparators.Length; + string firstLiteralMatch = null; + + var sbHashKey = new StringBuilder(); + for (var i = 0; i < components.Length; i++) + { + var component = components[i]; + + if (component.StartsWith(VariablePrefix)) + { + var variableName = component.Substring(1, component.Length - 2); + if (variableName[variableName.Length - 1] == WildCardChar) + { + this.isWildcard[i] = true; + variableName = variableName.Substring(0, variableName.Length - 1); + } + this.variablesNames[i] = variableName; + this.VariableArgsCount++; + } + else + { + this.literalsToMatch[i] = component.ToLower(); + sbHashKey.Append(i + PathSeperatorChar.ToString() + this.literalsToMatch); + + if (firstLiteralMatch == null) + { + firstLiteralMatch = this.literalsToMatch[i]; + } + } + } + + for (var i = 0; i < components.Length - 1; i++) + { + if (!this.isWildcard[i]) continue; + if (this.literalsToMatch[i + 1] == null) + { + throw new ArgumentException( + "A wildcard path component must be at the end of the path or followed by a literal path component."); + } + } + + this.wildcardCount = this.isWildcard.Count(x => x); + this.IsWildCardPath = this.wildcardCount > 0; + + this.FirstMatchHashKey = !this.IsWildCardPath + ? this.PathComponentsCount + PathSeperator + firstLiteralMatch + : WildCardChar + PathSeperator + firstLiteralMatch; + + this.IsValid = sbHashKey.Length > 0; + this.UniqueMatchHashKey = sbHashKey.ToString(); + + this.typeDeserializer = new StringMapTypeDeserializer(createInstanceFn, getParseFn, this.RequestType); + RegisterCaseInsenstivePropertyNameMappings(); + } + + private void RegisterCaseInsenstivePropertyNameMappings() + { + foreach (var propertyInfo in GetSerializableProperties(RequestType)) + { + var propertyName = propertyInfo.Name; + propertyNamesMap.Add(propertyName.ToLower(), propertyName); + } + } + + internal static string[] IgnoreAttributesNamed = new[] { + "IgnoreDataMemberAttribute", + "JsonIgnoreAttribute" + }; + + + private static List _excludeTypes = new List { typeof(Stream) }; + + internal static PropertyInfo[] GetSerializableProperties(Type type) + { + var properties = GetPublicProperties(type); + var readableProperties = properties.Where(x => x.GetMethod != null); + + // else return those properties that are not decorated with IgnoreDataMember + return readableProperties + .Where(prop => prop.GetCustomAttributes(true) + .All(attr => + { + var name = attr.GetType().Name; + return !IgnoreAttributesNamed.Contains(name); + })) + .Where(prop => !_excludeTypes.Contains(prop.PropertyType)) + .ToArray(); + } + + private static PropertyInfo[] GetPublicProperties(Type type) + { + if (type.GetTypeInfo().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.GetTypeInfo().ImplementedInterfaces) + { + if (considered.Contains(subInterface)) continue; + + considered.Add(subInterface); + queue.Enqueue(subInterface); + } + + var typeProperties = GetTypesPublicProperties(subType); + + var newPropertyInfos = typeProperties + .Where(x => !propertyInfos.Contains(x)); + + propertyInfos.InsertRange(0, newPropertyInfos); + } + + return propertyInfos.ToArray(); + } + + return GetTypesPublicProperties(type) + .Where(t => t.GetIndexParameters().Length == 0) // ignore indexed properties + .ToArray(); + } + + private static PropertyInfo[] GetTypesPublicProperties(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 bool IsValid { get; set; } + + /// + /// Provide for quick lookups based on hashes that can be determined from a request url + /// + public string FirstMatchHashKey { get; private set; } + + public string UniqueMatchHashKey { get; private set; } + + private readonly StringMapTypeDeserializer typeDeserializer; + + private readonly Dictionary propertyNamesMap = new Dictionary(); + + public int MatchScore(string httpMethod, string[] withPathInfoParts, ILogger logger) + { + int wildcardMatchCount; + var isMatch = IsMatch(httpMethod, withPathInfoParts, logger, out wildcardMatchCount); + if (!isMatch) + { + return -1; + } + + var score = 0; + + //Routes with least wildcard matches get the highest score + score += Math.Max((100 - wildcardMatchCount), 1) * 1000; + + //Routes with less variable (and more literal) matches + score += Math.Max((10 - VariableArgsCount), 1) * 100; + + //Exact verb match is better than ANY + var exactVerb = String.Equals(httpMethod, AllowedVerbs, StringComparison.OrdinalIgnoreCase); + score += exactVerb ? 10 : 1; + + return score; + } + + private bool StringContains(string str1, string str2) + { + return str1.IndexOf(str2, StringComparison.OrdinalIgnoreCase) != -1; + } + + /// + /// For performance withPathInfoParts should already be a lower case string + /// to minimize redundant matching operations. + /// + public bool IsMatch(string httpMethod, string[] withPathInfoParts, ILogger logger, out int wildcardMatchCount) + { + wildcardMatchCount = 0; + + if (withPathInfoParts.Length != this.PathComponentsCount && !this.IsWildCardPath) + { + //logger.Info("withPathInfoParts mismatch for {0} for {1}", httpMethod, string.Join("/", withPathInfoParts)); + return false; + } + + if (!this.allowsAllVerbs && !StringContains(this.allowedVerbs, httpMethod)) + { + //logger.Info("allowsAllVerbs mismatch for {0} for {1} allowedverbs {2}", httpMethod, string.Join("/", withPathInfoParts), this.allowedVerbs); + return false; + } + + if (!ExplodeComponents(ref withPathInfoParts)) + { + //logger.Info("ExplodeComponents mismatch for {0} for {1}", httpMethod, string.Join("/", withPathInfoParts)); + return false; + } + + if (this.TotalComponentsCount != withPathInfoParts.Length && !this.IsWildCardPath) + { + //logger.Info("TotalComponentsCount mismatch for {0} for {1}", httpMethod, string.Join("/", withPathInfoParts)); + return false; + } + + int pathIx = 0; + for (var i = 0; i < this.TotalComponentsCount; i++) + { + if (this.isWildcard[i]) + { + if (i < this.TotalComponentsCount - 1) + { + // Continue to consume up until a match with the next literal + while (pathIx < withPathInfoParts.Length && !LiteralsEqual(withPathInfoParts[pathIx], this.literalsToMatch[i + 1])) + { + pathIx++; + wildcardMatchCount++; + } + + // Ensure there are still enough parts left to match the remainder + if ((withPathInfoParts.Length - pathIx) < (this.TotalComponentsCount - i - 1)) + { + //logger.Info("withPathInfoParts length mismatch for {0} for {1}", httpMethod, string.Join("/", withPathInfoParts)); + return false; + } + } + else + { + // A wildcard at the end matches the remainder of path + wildcardMatchCount += withPathInfoParts.Length - pathIx; + pathIx = withPathInfoParts.Length; + } + } + else + { + var literalToMatch = this.literalsToMatch[i]; + if (literalToMatch == null) + { + // Matching an ordinary (non-wildcard) variable consumes a single part + pathIx++; + continue; + } + + if (withPathInfoParts.Length <= pathIx || !LiteralsEqual(withPathInfoParts[pathIx], literalToMatch)) + { + //logger.Info("withPathInfoParts2 length mismatch for {0} for {1}. not equals: {2} != {3}.", httpMethod, string.Join("/", withPathInfoParts), withPathInfoParts[pathIx], literalToMatch); + return false; + } + pathIx++; + } + } + + return pathIx == withPathInfoParts.Length; + } + + private bool LiteralsEqual(string str1, string str2) + { + // Most cases + if (String.Equals(str1, str2, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + // Handle turkish i + str1 = str1.ToUpperInvariant(); + str2 = str2.ToUpperInvariant(); + + // Invariant IgnoreCase would probably be better but it's not available in PCL + return String.Equals(str1, str2, StringComparison.CurrentCultureIgnoreCase); + } + + private bool ExplodeComponents(ref string[] withPathInfoParts) + { + var totalComponents = new List(); + for (var i = 0; i < withPathInfoParts.Length; i++) + { + var component = withPathInfoParts[i]; + if (String.IsNullOrEmpty(component)) continue; + + if (this.PathComponentsCount != this.TotalComponentsCount + && this.componentsWithSeparators[i]) + { + var subComponents = component.Split(ComponentSeperator); + if (subComponents.Length < 2) return false; + totalComponents.AddRange(subComponents); + } + else + { + totalComponents.Add(component); + } + } + + withPathInfoParts = totalComponents.ToArray(); + return true; + } + + public object CreateRequest(string pathInfo, Dictionary queryStringAndFormData, object fromInstance) + { + var requestComponents = pathInfo.Split(PathSeperatorChar) + .Where(x => !String.IsNullOrEmpty(x)).ToArray(); + + ExplodeComponents(ref requestComponents); + + if (requestComponents.Length != this.TotalComponentsCount) + { + var isValidWildCardPath = this.IsWildCardPath + && requestComponents.Length >= this.TotalComponentsCount - this.wildcardCount; + + if (!isValidWildCardPath) + throw new ArgumentException(String.Format( + "Path Mismatch: Request Path '{0}' has invalid number of components compared to: '{1}'", + pathInfo, this.restPath)); + } + + var requestKeyValuesMap = new Dictionary(); + var pathIx = 0; + for (var i = 0; i < this.TotalComponentsCount; i++) + { + var variableName = this.variablesNames[i]; + if (variableName == null) + { + pathIx++; + continue; + } + + string propertyNameOnRequest; + if (!this.propertyNamesMap.TryGetValue(variableName.ToLower(), out propertyNameOnRequest)) + { + if (String.Equals("ignore", variableName, StringComparison.OrdinalIgnoreCase)) + { + pathIx++; + continue; + } + + throw new ArgumentException("Could not find property " + + variableName + " on " + RequestType.GetMethodName()); + } + + var value = requestComponents.Length > pathIx ? requestComponents[pathIx] : null; //wildcard has arg mismatch + if (value != null && this.isWildcard[i]) + { + if (i == this.TotalComponentsCount - 1) + { + // Wildcard at end of path definition consumes all the rest + var sb = new StringBuilder(); + sb.Append(value); + for (var j = pathIx + 1; j < requestComponents.Length; j++) + { + sb.Append(PathSeperatorChar + requestComponents[j]); + } + value = sb.ToString(); + } + else + { + // Wildcard in middle of path definition consumes up until it + // hits a match for the next element in the definition (which must be a literal) + // It may consume 0 or more path parts + var stopLiteral = i == this.TotalComponentsCount - 1 ? null : this.literalsToMatch[i + 1]; + if (!String.Equals(requestComponents[pathIx], stopLiteral, StringComparison.OrdinalIgnoreCase)) + { + var sb = new StringBuilder(); + sb.Append(value); + pathIx++; + while (!String.Equals(requestComponents[pathIx], stopLiteral, StringComparison.OrdinalIgnoreCase)) + { + sb.Append(PathSeperatorChar + requestComponents[pathIx++]); + } + value = sb.ToString(); + } + else + { + value = null; + } + } + } + else + { + // Variable consumes single path item + pathIx++; + } + + requestKeyValuesMap[propertyNameOnRequest] = value; + } + + if (queryStringAndFormData != null) + { + //Query String and form data can override variable path matches + //path variables < query string < form data + foreach (var name in queryStringAndFormData) + { + requestKeyValuesMap[name.Key] = name.Value; + } + } + + return this.typeDeserializer.PopulateFromMap(fromInstance, requestKeyValuesMap); + } + + public override int GetHashCode() + { + return UniqueMatchHashKey.GetHashCode(); + } + } +} \ No newline at end of file diff --git a/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs b/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs new file mode 100644 index 000000000..fc1cf4ed9 --- /dev/null +++ b/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Emby.Server.Implementations.Services +{ + /// + /// Serializer cache of delegates required to create a type from a string map (e.g. for REST urls) + /// + public class StringMapTypeDeserializer + { + internal class PropertySerializerEntry + { + public PropertySerializerEntry(Action propertySetFn, Func propertyParseStringFn) + { + PropertySetFn = propertySetFn; + PropertyParseStringFn = propertyParseStringFn; + } + + public Action PropertySetFn; + public Func PropertyParseStringFn; + public Type PropertyType; + } + + private readonly Type type; + private readonly Dictionary propertySetterMap + = new Dictionary(StringComparer.OrdinalIgnoreCase); + + public Func GetParseFn(Type propertyType) + { + if (propertyType == typeof(string)) + return s => s; + + return _GetParseFn(propertyType); + } + + private readonly Func _CreateInstanceFn; + private readonly Func> _GetParseFn; + + public StringMapTypeDeserializer(Func createInstanceFn, Func> getParseFn, Type type) + { + _CreateInstanceFn = createInstanceFn; + _GetParseFn = getParseFn; + this.type = type; + + foreach (var propertyInfo in RestPath.GetSerializableProperties(type)) + { + var propertySetFn = TypeAccessor.GetSetPropertyMethod(type, propertyInfo); + var propertyType = propertyInfo.PropertyType; + var propertyParseStringFn = GetParseFn(propertyType); + var propertySerializer = new PropertySerializerEntry(propertySetFn, propertyParseStringFn) { PropertyType = propertyType }; + + propertySetterMap[propertyInfo.Name] = propertySerializer; + } + } + + public object PopulateFromMap(object instance, IDictionary keyValuePairs) + { + string propertyName = null; + string propertyTextValue = null; + PropertySerializerEntry propertySerializerEntry = null; + + if (instance == null) + instance = _CreateInstanceFn(type); + + foreach (var pair in keyValuePairs.Where(x => !string.IsNullOrEmpty(x.Value))) + { + propertyName = pair.Key; + propertyTextValue = pair.Value; + + if (!propertySetterMap.TryGetValue(propertyName, out propertySerializerEntry)) + { + if (propertyName == "v") + { + continue; + } + + continue; + } + + if (propertySerializerEntry.PropertySetFn == null) + { + continue; + } + + if (propertySerializerEntry.PropertyType == typeof(bool)) + { + //InputExtensions.cs#530 MVC Checkbox helper emits extra hidden input field, generating 2 values, first is the real value + propertyTextValue = LeftPart(propertyTextValue, ','); + } + + var value = propertySerializerEntry.PropertyParseStringFn(propertyTextValue); + if (value == null) + { + continue; + } + propertySerializerEntry.PropertySetFn(instance, value); + } + + return instance; + } + + public static string LeftPart(string strVal, char needle) + { + if (strVal == null) return null; + var pos = strVal.IndexOf(needle); + return pos == -1 + ? strVal + : strVal.Substring(0, pos); + } + } + + internal class TypeAccessor + { + public static Action GetSetPropertyMethod(Type type, PropertyInfo propertyInfo) + { + if (!propertyInfo.CanWrite || propertyInfo.GetIndexParameters().Any()) return null; + + var setMethodInfo = propertyInfo.SetMethod; + return (instance, value) => setMethodInfo.Invoke(instance, new[] { value }); + } + } +} diff --git a/Emby.Server.Implementations/Services/UrlExtensions.cs b/Emby.Server.Implementations/Services/UrlExtensions.cs new file mode 100644 index 000000000..c7346789a --- /dev/null +++ b/Emby.Server.Implementations/Services/UrlExtensions.cs @@ -0,0 +1,33 @@ +using System; + +namespace Emby.Server.Implementations.Services +{ + /// + /// Donated by Ivan Korneliuk from his post: + /// http://korneliuk.blogspot.com/2012/08/servicestack-reusing-dtos.html + /// + /// Modified to only allow using routes matching the supplied HTTP Verb + /// + public static class UrlExtensions + { + public static string GetMethodName(this Type type) + { + var typeName = type.FullName != null //can be null, e.g. generic types + ? LeftPart(type.FullName, "[[") //Generic Fullname + .Replace(type.Namespace + ".", "") //Trim Namespaces + .Replace("+", ".") //Convert nested into normal type + : type.Name; + + return type.IsGenericParameter ? "'" + typeName : typeName; + } + + public static string LeftPart(string strVal, string needle) + { + if (strVal == null) return null; + var pos = strVal.IndexOf(needle, StringComparison.OrdinalIgnoreCase); + return pos == -1 + ? strVal + : strVal.Substring(0, pos); + } + } +} \ No newline at end of file diff --git a/MediaBrowser.Mono.sln b/MediaBrowser.Mono.sln index 35c396200..66ae294a7 100644 --- a/MediaBrowser.Mono.sln +++ b/MediaBrowser.Mono.sln @@ -47,8 +47,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing.ImageMagick", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing.Net", "Emby.Drawing.Net\Emby.Drawing.Net.csproj", "{C97A239E-A96C-4D64-A844-CCF8CC30AECB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceStack", "ServiceStack\ServiceStack.csproj", "{680A1709-25EB-4D52-A87F-EE03FFD94BAA}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SocketHttpListener.Portable", "SocketHttpListener.Portable\SocketHttpListener.Portable.csproj", "{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}" EndProject Global @@ -386,22 +384,6 @@ Global {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|Any CPU.Build.0 = Release|Any CPU {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|x86.ActiveCfg = Release|Any CPU {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|x86.Build.0 = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|x86.ActiveCfg = Debug|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|x86.Build.0 = Debug|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Any CPU.Build.0 = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|x86.ActiveCfg = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|x86.Build.0 = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Any CPU.Build.0 = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|x86.ActiveCfg = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|x86.Build.0 = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Any CPU.ActiveCfg = Signed|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Any CPU.Build.0 = Signed|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|x86.ActiveCfg = Signed|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|x86.Build.0 = Signed|Any CPU {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|Any CPU.Build.0 = Debug|Any CPU {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|x86.ActiveCfg = Debug|Any CPU diff --git a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs index ec31824db..30617643a 100644 --- a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs +++ b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs @@ -45,6 +45,7 @@ namespace MediaBrowser.Providers.Music public async Task> GetSearchResults(AlbumInfo searchInfo, CancellationToken cancellationToken) { var releaseId = searchInfo.GetReleaseId(); + var releaseGroupId = searchInfo.GetReleaseGroupId(); string url = null; var isNameSearch = false; @@ -53,6 +54,10 @@ namespace MediaBrowser.Providers.Music { url = string.Format("/ws/2/release/?query=reid:{0}", releaseId); } + else if (!string.IsNullOrEmpty(releaseGroupId)) + { + url = string.Format("/ws/2/release?release-group={0}", releaseGroupId); + } else { var artistMusicBrainzId = searchInfo.GetMusicBrainzArtistId(); @@ -131,7 +136,14 @@ namespace MediaBrowser.Providers.Music Item = new MusicAlbum() }; - if (string.IsNullOrEmpty(releaseId)) + // If we have a release group Id but not a release Id... + if (string.IsNullOrWhiteSpace(releaseId) && !string.IsNullOrWhiteSpace(releaseGroupId)) + { + releaseId = await GetReleaseIdFromReleaseGroupId(releaseGroupId, cancellationToken).ConfigureAwait(false); + result.HasMetadata = true; + } + + if (string.IsNullOrWhiteSpace(releaseId)) { var artistMusicBrainzId = id.GetMusicBrainzArtistId(); @@ -139,13 +151,13 @@ namespace MediaBrowser.Providers.Music if (releaseResult != null) { - if (!string.IsNullOrEmpty(releaseResult.ReleaseId)) + if (!string.IsNullOrWhiteSpace(releaseResult.ReleaseId)) { releaseId = releaseResult.ReleaseId; result.HasMetadata = true; } - if (!string.IsNullOrEmpty(releaseResult.ReleaseGroupId)) + if (!string.IsNullOrWhiteSpace(releaseResult.ReleaseGroupId)) { releaseGroupId = releaseResult.ReleaseGroupId; result.HasMetadata = true; @@ -157,13 +169,13 @@ namespace MediaBrowser.Providers.Music } // If we have a release Id but not a release group Id... - if (!string.IsNullOrEmpty(releaseId) && string.IsNullOrEmpty(releaseGroupId)) + if (!string.IsNullOrWhiteSpace(releaseId) && string.IsNullOrWhiteSpace(releaseGroupId)) { - releaseGroupId = await GetReleaseGroupId(releaseId, cancellationToken).ConfigureAwait(false); + releaseGroupId = await GetReleaseGroupFromReleaseId(releaseId, cancellationToken).ConfigureAwait(false); result.HasMetadata = true; } - if (!string.IsNullOrEmpty(releaseId) || !string.IsNullOrEmpty(releaseGroupId)) + if (!string.IsNullOrWhiteSpace(releaseId) || !string.IsNullOrWhiteSpace(releaseGroupId)) { result.HasMetadata = true; } @@ -411,13 +423,42 @@ namespace MediaBrowser.Providers.Music } } + private async Task GetReleaseIdFromReleaseGroupId(string releaseGroupId, CancellationToken cancellationToken) + { + var url = string.Format("/ws/2/release?release-group={0}", releaseGroupId); + + using (var stream = await GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false)) + { + using (var oReader = new StreamReader(stream, Encoding.UTF8)) + { + var settings = _xmlSettings.Create(false); + + settings.CheckCharacters = false; + settings.IgnoreProcessingInstructions = true; + settings.IgnoreComments = true; + + using (var reader = XmlReader.Create(oReader, settings)) + { + var result = ReleaseResult.Parse(reader).FirstOrDefault(); + + if (result != null) + { + return result.ReleaseId; + } + } + } + } + + return null; + } + /// /// Gets the release group id internal. /// /// The release entry id. /// The cancellation token. /// Task{System.String}. - private async Task GetReleaseGroupId(string releaseEntryId, CancellationToken cancellationToken) + private async Task GetReleaseGroupFromReleaseId(string releaseEntryId, CancellationToken cancellationToken) { var url = string.Format("/ws/2/release-group/?query=reid:{0}", releaseEntryId); diff --git a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj index 325011adf..27001d596 100644 --- a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj +++ b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj @@ -200,10 +200,6 @@ {21002819-c39a-4d3e-be83-2a276a77fb1f} RSSDP - - {680a1709-25eb-4d52-a87f-ee03ffd94baa} - ServiceStack - {4f26d5d8-a7b0-42b3-ba42-7cb7d245934e} SocketHttpListener.Portable diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index 7badccef3..8a75bf67a 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -1155,10 +1155,6 @@ {21002819-c39a-4d3e-be83-2a276a77fb1f} RSSDP - - {680a1709-25eb-4d52-a87f-ee03ffd94baa} - ServiceStack - {4f26d5d8-a7b0-42b3-ba42-7cb7d245934e} SocketHttpListener.Portable diff --git a/MediaBrowser.sln b/MediaBrowser.sln index 292d0345c..b9933969f 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -78,8 +78,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing.ImageMagick", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing.Net", "Emby.Drawing.Net\Emby.Drawing.Net.csproj", "{C97A239E-A96C-4D64-A844-CCF8CC30AECB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceStack", "ServiceStack\ServiceStack.csproj", "{680A1709-25EB-4D52-A87F-EE03FFD94BAA}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SocketHttpListener.Portable", "SocketHttpListener.Portable\SocketHttpListener.Portable.csproj", "{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}" EndProject Global @@ -1061,46 +1059,6 @@ Global {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|x64.Build.0 = Release|Any CPU {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|x86.ActiveCfg = Release|Any CPU {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|x86.Build.0 = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Win32.ActiveCfg = Debug|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Win32.Build.0 = Debug|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|x64.ActiveCfg = Debug|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|x64.Build.0 = Debug|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|x86.ActiveCfg = Debug|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|x86.Build.0 = Debug|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Any CPU.Build.0 = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Win32.ActiveCfg = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Win32.Build.0 = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|x64.ActiveCfg = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|x64.Build.0 = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|x86.ActiveCfg = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|x86.Build.0 = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Any CPU.Build.0 = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Win32.ActiveCfg = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Win32.Build.0 = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|x64.ActiveCfg = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|x64.Build.0 = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|x86.ActiveCfg = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|x86.Build.0 = Release|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Any CPU.ActiveCfg = Signed|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Any CPU.Build.0 = Signed|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Mixed Platforms.ActiveCfg = Signed|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Mixed Platforms.Build.0 = Signed|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Win32.ActiveCfg = Signed|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Win32.Build.0 = Signed|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|x64.ActiveCfg = Signed|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|x64.Build.0 = Signed|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|x86.ActiveCfg = Signed|Any CPU - {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|x86.Build.0 = Signed|Any CPU {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|Any CPU.Build.0 = Debug|Any CPU {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU diff --git a/ServiceStack/Properties/AssemblyInfo.cs b/ServiceStack/Properties/AssemblyInfo.cs deleted file mode 100644 index 6073dc0b4..000000000 --- a/ServiceStack/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("ServiceStack")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Service Stack LLC")] -[assembly: AssemblyProduct("ServiceStack")] -[assembly: AssemblyCopyright("Copyright (c) ServiceStack 2016")] -[assembly: AssemblyTrademark("Service Stack")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("06704d66-af8e-411f-8260-8d05de5ce6ad")] - -[assembly: AssemblyVersion("4.0.0.0")] -[assembly: AssemblyFileVersion("4.0.0.0")] diff --git a/ServiceStack/RestPath.cs b/ServiceStack/RestPath.cs deleted file mode 100644 index afd1f73e1..000000000 --- a/ServiceStack/RestPath.cs +++ /dev/null @@ -1,564 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using MediaBrowser.Model.Logging; -using ServiceStack.Serialization; - -namespace ServiceStack -{ - public class RestPath - { - private const string WildCard = "*"; - private const char WildCardChar = '*'; - private const string PathSeperator = "/"; - private const char PathSeperatorChar = '/'; - private const char ComponentSeperator = '.'; - private const string VariablePrefix = "{"; - - readonly bool[] componentsWithSeparators; - - private readonly string restPath; - private readonly string allowedVerbs; - private readonly bool allowsAllVerbs; - public bool IsWildCardPath { get; private set; } - - private readonly string[] literalsToMatch; - - private readonly string[] variablesNames; - - private readonly bool[] isWildcard; - private readonly int wildcardCount = 0; - - public int VariableArgsCount { get; set; } - - /// - /// The number of segments separated by '/' determinable by path.Split('/').Length - /// e.g. /path/to/here.ext == 3 - /// - public int PathComponentsCount { get; set; } - - /// - /// The total number of segments after subparts have been exploded ('.') - /// e.g. /path/to/here.ext == 4 - /// - public int TotalComponentsCount { get; set; } - - public string[] Verbs - { - get - { - return allowsAllVerbs - ? new[] { "ANY" } - : AllowedVerbs.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); - } - } - - public Type RequestType { get; private set; } - - public string Path { get { return this.restPath; } } - - public string Summary { get; private set; } - - public string Notes { get; private set; } - - public bool AllowsAllVerbs { get { return this.allowsAllVerbs; } } - - public string AllowedVerbs { get { return this.allowedVerbs; } } - - public int Priority { get; set; } //passed back to RouteAttribute - - public static string[] GetPathPartsForMatching(string pathInfo) - { - var parts = pathInfo.ToLower().Split(PathSeperatorChar) - .Where(x => !String.IsNullOrEmpty(x)).ToArray(); - return parts; - } - - public static List GetFirstMatchHashKeys(string[] pathPartsForMatching) - { - var hashPrefix = pathPartsForMatching.Length + PathSeperator; - return GetPotentialMatchesWithPrefix(hashPrefix, pathPartsForMatching); - } - - public static List GetFirstMatchWildCardHashKeys(string[] pathPartsForMatching) - { - const string hashPrefix = WildCard + PathSeperator; - return GetPotentialMatchesWithPrefix(hashPrefix, pathPartsForMatching); - } - - private static List GetPotentialMatchesWithPrefix(string hashPrefix, string[] pathPartsForMatching) - { - var list = new List(); - - foreach (var part in pathPartsForMatching) - { - list.Add(hashPrefix + part); - - var subParts = part.Split(ComponentSeperator); - if (subParts.Length == 1) continue; - - foreach (var subPart in subParts) - { - list.Add(hashPrefix + subPart); - } - } - - return list; - } - - public RestPath(Func createInstanceFn, Func> getParseFn, Type requestType, string path, string verbs, string summary = null, string notes = null) - { - this.RequestType = requestType; - this.Summary = summary; - this.Notes = notes; - this.restPath = path; - - this.allowsAllVerbs = verbs == null || String.Equals(verbs, WildCard, StringComparison.OrdinalIgnoreCase); - if (!this.allowsAllVerbs) - { - this.allowedVerbs = verbs.ToUpper(); - } - - var componentsList = new List(); - - //We only split on '.' if the restPath has them. Allows for /{action}.{type} - var hasSeparators = new List(); - foreach (var component in this.restPath.Split(PathSeperatorChar)) - { - if (String.IsNullOrEmpty(component)) continue; - - if (StringContains(component, VariablePrefix) - && component.IndexOf(ComponentSeperator) != -1) - { - hasSeparators.Add(true); - componentsList.AddRange(component.Split(ComponentSeperator)); - } - else - { - hasSeparators.Add(false); - componentsList.Add(component); - } - } - - var components = componentsList.ToArray(); - this.TotalComponentsCount = components.Length; - - this.literalsToMatch = new string[this.TotalComponentsCount]; - this.variablesNames = new string[this.TotalComponentsCount]; - this.isWildcard = new bool[this.TotalComponentsCount]; - this.componentsWithSeparators = hasSeparators.ToArray(); - this.PathComponentsCount = this.componentsWithSeparators.Length; - string firstLiteralMatch = null; - - var sbHashKey = new StringBuilder(); - for (var i = 0; i < components.Length; i++) - { - var component = components[i]; - - if (component.StartsWith(VariablePrefix)) - { - var variableName = component.Substring(1, component.Length - 2); - if (variableName[variableName.Length - 1] == WildCardChar) - { - this.isWildcard[i] = true; - variableName = variableName.Substring(0, variableName.Length - 1); - } - this.variablesNames[i] = variableName; - this.VariableArgsCount++; - } - else - { - this.literalsToMatch[i] = component.ToLower(); - sbHashKey.Append(i + PathSeperatorChar.ToString() + this.literalsToMatch); - - if (firstLiteralMatch == null) - { - firstLiteralMatch = this.literalsToMatch[i]; - } - } - } - - for (var i = 0; i < components.Length - 1; i++) - { - if (!this.isWildcard[i]) continue; - if (this.literalsToMatch[i + 1] == null) - { - throw new ArgumentException( - "A wildcard path component must be at the end of the path or followed by a literal path component."); - } - } - - this.wildcardCount = this.isWildcard.Count(x => x); - this.IsWildCardPath = this.wildcardCount > 0; - - this.FirstMatchHashKey = !this.IsWildCardPath - ? this.PathComponentsCount + PathSeperator + firstLiteralMatch - : WildCardChar + PathSeperator + firstLiteralMatch; - - this.IsValid = sbHashKey.Length > 0; - this.UniqueMatchHashKey = sbHashKey.ToString(); - - this.typeDeserializer = new StringMapTypeDeserializer(createInstanceFn, getParseFn, this.RequestType); - RegisterCaseInsenstivePropertyNameMappings(); - } - - private void RegisterCaseInsenstivePropertyNameMappings() - { - foreach (var propertyInfo in GetSerializableProperties(RequestType)) - { - var propertyName = propertyInfo.Name; - propertyNamesMap.Add(propertyName.ToLower(), propertyName); - } - } - - internal static string[] IgnoreAttributesNamed = new[] { - "IgnoreDataMemberAttribute", - "JsonIgnoreAttribute" - }; - - - private static List _excludeTypes = new List { typeof(Stream) }; - - internal static PropertyInfo[] GetSerializableProperties(Type type) - { - var properties = GetPublicProperties(type); - var readableProperties = properties.Where(x => x.GetMethod != null); - - // else return those properties that are not decorated with IgnoreDataMember - return readableProperties - .Where(prop => prop.GetCustomAttributes(true) - .All(attr => - { - var name = attr.GetType().Name; - return !IgnoreAttributesNamed.Contains(name); - })) - .Where(prop => !_excludeTypes.Contains(prop.PropertyType)) - .ToArray(); - } - - private static PropertyInfo[] GetPublicProperties(Type type) - { - if (type.GetTypeInfo().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.GetTypeInfo().ImplementedInterfaces) - { - if (considered.Contains(subInterface)) continue; - - considered.Add(subInterface); - queue.Enqueue(subInterface); - } - - var typeProperties = GetTypesPublicProperties(subType); - - var newPropertyInfos = typeProperties - .Where(x => !propertyInfos.Contains(x)); - - propertyInfos.InsertRange(0, newPropertyInfos); - } - - return propertyInfos.ToArray(); - } - - return GetTypesPublicProperties(type) - .Where(t => t.GetIndexParameters().Length == 0) // ignore indexed properties - .ToArray(); - } - - private static PropertyInfo[] GetTypesPublicProperties(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 bool IsValid { get; set; } - - /// - /// Provide for quick lookups based on hashes that can be determined from a request url - /// - public string FirstMatchHashKey { get; private set; } - - public string UniqueMatchHashKey { get; private set; } - - private readonly StringMapTypeDeserializer typeDeserializer; - - private readonly Dictionary propertyNamesMap = new Dictionary(); - - public int MatchScore(string httpMethod, string[] withPathInfoParts, ILogger logger) - { - int wildcardMatchCount; - var isMatch = IsMatch(httpMethod, withPathInfoParts, logger, out wildcardMatchCount); - if (!isMatch) - { - return -1; - } - - var score = 0; - - //Routes with least wildcard matches get the highest score - score += Math.Max((100 - wildcardMatchCount), 1) * 1000; - - //Routes with less variable (and more literal) matches - score += Math.Max((10 - VariableArgsCount), 1) * 100; - - //Exact verb match is better than ANY - var exactVerb = String.Equals(httpMethod, AllowedVerbs, StringComparison.OrdinalIgnoreCase); - score += exactVerb ? 10 : 1; - - return score; - } - - private bool StringContains(string str1, string str2) - { - return str1.IndexOf(str2, StringComparison.OrdinalIgnoreCase) != -1; - } - - /// - /// For performance withPathInfoParts should already be a lower case string - /// to minimize redundant matching operations. - /// - public bool IsMatch(string httpMethod, string[] withPathInfoParts, ILogger logger, out int wildcardMatchCount) - { - wildcardMatchCount = 0; - - if (withPathInfoParts.Length != this.PathComponentsCount && !this.IsWildCardPath) - { - //logger.Info("withPathInfoParts mismatch for {0} for {1}", httpMethod, string.Join("/", withPathInfoParts)); - return false; - } - - if (!this.allowsAllVerbs && !StringContains(this.allowedVerbs, httpMethod)) - { - //logger.Info("allowsAllVerbs mismatch for {0} for {1} allowedverbs {2}", httpMethod, string.Join("/", withPathInfoParts), this.allowedVerbs); - return false; - } - - if (!ExplodeComponents(ref withPathInfoParts)) - { - //logger.Info("ExplodeComponents mismatch for {0} for {1}", httpMethod, string.Join("/", withPathInfoParts)); - return false; - } - - if (this.TotalComponentsCount != withPathInfoParts.Length && !this.IsWildCardPath) - { - //logger.Info("TotalComponentsCount mismatch for {0} for {1}", httpMethod, string.Join("/", withPathInfoParts)); - return false; - } - - int pathIx = 0; - for (var i = 0; i < this.TotalComponentsCount; i++) - { - if (this.isWildcard[i]) - { - if (i < this.TotalComponentsCount - 1) - { - // Continue to consume up until a match with the next literal - while (pathIx < withPathInfoParts.Length && !LiteralsEqual(withPathInfoParts[pathIx], this.literalsToMatch[i + 1])) - { - pathIx++; - wildcardMatchCount++; - } - - // Ensure there are still enough parts left to match the remainder - if ((withPathInfoParts.Length - pathIx) < (this.TotalComponentsCount - i - 1)) - { - //logger.Info("withPathInfoParts length mismatch for {0} for {1}", httpMethod, string.Join("/", withPathInfoParts)); - return false; - } - } - else - { - // A wildcard at the end matches the remainder of path - wildcardMatchCount += withPathInfoParts.Length - pathIx; - pathIx = withPathInfoParts.Length; - } - } - else - { - var literalToMatch = this.literalsToMatch[i]; - if (literalToMatch == null) - { - // Matching an ordinary (non-wildcard) variable consumes a single part - pathIx++; - continue; - } - - if (withPathInfoParts.Length <= pathIx || !LiteralsEqual(withPathInfoParts[pathIx], literalToMatch)) - { - //logger.Info("withPathInfoParts2 length mismatch for {0} for {1}. not equals: {2} != {3}.", httpMethod, string.Join("/", withPathInfoParts), withPathInfoParts[pathIx], literalToMatch); - return false; - } - pathIx++; - } - } - - return pathIx == withPathInfoParts.Length; - } - - private bool LiteralsEqual(string str1, string str2) - { - // Most cases - if (String.Equals(str1, str2, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - // Handle turkish i - str1 = str1.ToUpperInvariant(); - str2 = str2.ToUpperInvariant(); - - // Invariant IgnoreCase would probably be better but it's not available in PCL - return String.Equals(str1, str2, StringComparison.CurrentCultureIgnoreCase); - } - - private bool ExplodeComponents(ref string[] withPathInfoParts) - { - var totalComponents = new List(); - for (var i = 0; i < withPathInfoParts.Length; i++) - { - var component = withPathInfoParts[i]; - if (String.IsNullOrEmpty(component)) continue; - - if (this.PathComponentsCount != this.TotalComponentsCount - && this.componentsWithSeparators[i]) - { - var subComponents = component.Split(ComponentSeperator); - if (subComponents.Length < 2) return false; - totalComponents.AddRange(subComponents); - } - else - { - totalComponents.Add(component); - } - } - - withPathInfoParts = totalComponents.ToArray(); - return true; - } - - public object CreateRequest(string pathInfo, Dictionary queryStringAndFormData, object fromInstance) - { - var requestComponents = pathInfo.Split(PathSeperatorChar) - .Where(x => !String.IsNullOrEmpty(x)).ToArray(); - - ExplodeComponents(ref requestComponents); - - if (requestComponents.Length != this.TotalComponentsCount) - { - var isValidWildCardPath = this.IsWildCardPath - && requestComponents.Length >= this.TotalComponentsCount - this.wildcardCount; - - if (!isValidWildCardPath) - throw new ArgumentException(String.Format( - "Path Mismatch: Request Path '{0}' has invalid number of components compared to: '{1}'", - pathInfo, this.restPath)); - } - - var requestKeyValuesMap = new Dictionary(); - var pathIx = 0; - for (var i = 0; i < this.TotalComponentsCount; i++) - { - var variableName = this.variablesNames[i]; - if (variableName == null) - { - pathIx++; - continue; - } - - string propertyNameOnRequest; - if (!this.propertyNamesMap.TryGetValue(variableName.ToLower(), out propertyNameOnRequest)) - { - if (String.Equals("ignore", variableName, StringComparison.OrdinalIgnoreCase)) - { - pathIx++; - continue; - } - - throw new ArgumentException("Could not find property " - + variableName + " on " + RequestType.GetOperationName()); - } - - var value = requestComponents.Length > pathIx ? requestComponents[pathIx] : null; //wildcard has arg mismatch - if (value != null && this.isWildcard[i]) - { - if (i == this.TotalComponentsCount - 1) - { - // Wildcard at end of path definition consumes all the rest - var sb = new StringBuilder(); - sb.Append(value); - for (var j = pathIx + 1; j < requestComponents.Length; j++) - { - sb.Append(PathSeperatorChar + requestComponents[j]); - } - value = sb.ToString(); - } - else - { - // Wildcard in middle of path definition consumes up until it - // hits a match for the next element in the definition (which must be a literal) - // It may consume 0 or more path parts - var stopLiteral = i == this.TotalComponentsCount - 1 ? null : this.literalsToMatch[i + 1]; - if (!String.Equals(requestComponents[pathIx], stopLiteral, StringComparison.OrdinalIgnoreCase)) - { - var sb = new StringBuilder(); - sb.Append(value); - pathIx++; - while (!String.Equals(requestComponents[pathIx], stopLiteral, StringComparison.OrdinalIgnoreCase)) - { - sb.Append(PathSeperatorChar + requestComponents[pathIx++]); - } - value = sb.ToString(); - } - else - { - value = null; - } - } - } - else - { - // Variable consumes single path item - pathIx++; - } - - requestKeyValuesMap[propertyNameOnRequest] = value; - } - - if (queryStringAndFormData != null) - { - //Query String and form data can override variable path matches - //path variables < query string < form data - foreach (var name in queryStringAndFormData) - { - requestKeyValuesMap[name.Key] = name.Value; - } - } - - return this.typeDeserializer.PopulateFromMap(fromInstance, requestKeyValuesMap); - } - - public override int GetHashCode() - { - return UniqueMatchHashKey.GetHashCode(); - } - } -} \ No newline at end of file diff --git a/ServiceStack/ServiceStack.csproj b/ServiceStack/ServiceStack.csproj deleted file mode 100644 index 33226971e..000000000 --- a/ServiceStack/ServiceStack.csproj +++ /dev/null @@ -1,115 +0,0 @@ - - - - Debug - AnyCPU - 9.0.30729 - 2.0 - {680A1709-25EB-4D52-A87F-EE03FFD94BAA} - Library - Properties - ServiceStack - ServiceStack - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Profile7 - v4.5 - 512 - - - 3.5 - - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - - - True - full - False - bin\Debug\ - TRACE;DEBUG;MONO - prompt - 4 - AllRules.ruleset - false - - - pdbonly - True - bin\Release\ - TRACE - prompt - 4 - AllRules.ruleset - - - false - - - bin\Signed\ - TRACE - bin\Release\ServiceStack.XML - true - pdbonly - AnyCPU - prompt - AllRules.ruleset - false - - - - - - - - - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - true - - - False - Windows Installer 3.1 - true - - - - - - - {9142eefa-7570-41e1-bfcc-468bb571af2f} - MediaBrowser.Common - - - {7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b} - MediaBrowser.Model - - - \ No newline at end of file diff --git a/ServiceStack/ServiceStack.nuget.targets b/ServiceStack/ServiceStack.nuget.targets deleted file mode 100644 index e69ce0e64..000000000 --- a/ServiceStack/ServiceStack.nuget.targets +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/ServiceStack/StringMapTypeDeserializer.cs b/ServiceStack/StringMapTypeDeserializer.cs deleted file mode 100644 index 82724fc4a..000000000 --- a/ServiceStack/StringMapTypeDeserializer.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.Serialization; -using System.Linq; -using System.Reflection; - -namespace ServiceStack.Serialization -{ - /// - /// Serializer cache of delegates required to create a type from a string map (e.g. for REST urls) - /// - public class StringMapTypeDeserializer - { - internal class PropertySerializerEntry - { - public PropertySerializerEntry(Action propertySetFn, Func propertyParseStringFn) - { - PropertySetFn = propertySetFn; - PropertyParseStringFn = propertyParseStringFn; - } - - public Action PropertySetFn; - public Func PropertyParseStringFn; - public Type PropertyType; - } - - private readonly Type type; - private readonly Dictionary propertySetterMap - = new Dictionary(StringComparer.OrdinalIgnoreCase); - - public Func GetParseFn(Type propertyType) - { - //Don't JSV-decode string values for string properties - if (propertyType == typeof(string)) - return s => s; - - return _GetParseFn(propertyType); - } - - private readonly Func _CreateInstanceFn; - private readonly Func> _GetParseFn; - - public StringMapTypeDeserializer(Func createInstanceFn, Func> getParseFn, Type type) - { - _CreateInstanceFn = createInstanceFn; - _GetParseFn = getParseFn; - this.type = type; - - foreach (var propertyInfo in RestPath.GetSerializableProperties(type)) - { - var propertySetFn = TypeAccessor.GetSetPropertyMethod(type, propertyInfo); - var propertyType = propertyInfo.PropertyType; - var propertyParseStringFn = GetParseFn(propertyType); - var propertySerializer = new PropertySerializerEntry(propertySetFn, propertyParseStringFn) { PropertyType = propertyType }; - - propertySetterMap[propertyInfo.Name] = propertySerializer; - } - } - - public object PopulateFromMap(object instance, IDictionary keyValuePairs) - { - string propertyName = null; - string propertyTextValue = null; - PropertySerializerEntry propertySerializerEntry = null; - - if (instance == null) - instance = _CreateInstanceFn(type); - - foreach (var pair in keyValuePairs.Where(x => !string.IsNullOrEmpty(x.Value))) - { - propertyName = pair.Key; - propertyTextValue = pair.Value; - - if (!propertySetterMap.TryGetValue(propertyName, out propertySerializerEntry)) - { - if (propertyName == "v") - { - continue; - } - - continue; - } - - if (propertySerializerEntry.PropertySetFn == null) - { - continue; - } - - if (propertySerializerEntry.PropertyType == typeof(bool)) - { - //InputExtensions.cs#530 MVC Checkbox helper emits extra hidden input field, generating 2 values, first is the real value - propertyTextValue = LeftPart(propertyTextValue, ','); - } - - var value = propertySerializerEntry.PropertyParseStringFn(propertyTextValue); - if (value == null) - { - continue; - } - propertySerializerEntry.PropertySetFn(instance, value); - } - - return instance; - } - - public static string LeftPart(string strVal, char needle) - { - if (strVal == null) return null; - var pos = strVal.IndexOf(needle); - return pos == -1 - ? strVal - : strVal.Substring(0, pos); - } - } - - internal class TypeAccessor - { - public static Action GetSetPropertyMethod(Type type, PropertyInfo propertyInfo) - { - if (!propertyInfo.CanWrite || propertyInfo.GetIndexParameters().Any()) return null; - - var setMethodInfo = propertyInfo.SetMethod; - return (instance, value) => setMethodInfo.Invoke(instance, new[] { value }); - } - } -} diff --git a/ServiceStack/UrlExtensions.cs b/ServiceStack/UrlExtensions.cs deleted file mode 100644 index 7b5a50ef1..000000000 --- a/ServiceStack/UrlExtensions.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; - -namespace ServiceStack -{ - /// - /// Donated by Ivan Korneliuk from his post: - /// http://korneliuk.blogspot.com/2012/08/servicestack-reusing-dtos.html - /// - /// Modified to only allow using routes matching the supplied HTTP Verb - /// - public static class UrlExtensions - { - public static string GetOperationName(this Type type) - { - var typeName = type.FullName != null //can be null, e.g. generic types - ? LeftPart(type.FullName, "[[") //Generic Fullname - .Replace(type.Namespace + ".", "") //Trim Namespaces - .Replace("+", ".") //Convert nested into normal type - : type.Name; - - return type.IsGenericParameter ? "'" + typeName : typeName; - } - - public static string LeftPart(string strVal, string needle) - { - if (strVal == null) return null; - var pos = strVal.IndexOf(needle, StringComparison.OrdinalIgnoreCase); - return pos == -1 - ? strVal - : strVal.Substring(0, pos); - } - } -} \ No newline at end of file diff --git a/ServiceStack/packages.config b/ServiceStack/packages.config deleted file mode 100644 index 6b8deb9c9..000000000 --- a/ServiceStack/packages.config +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/ServiceStack/project.json b/ServiceStack/project.json deleted file mode 100644 index fbbe9eaf3..000000000 --- a/ServiceStack/project.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "frameworks":{ - "netstandard1.6":{ - "dependencies":{ - "NETStandard.Library":"1.6.0", - } - }, - ".NETPortable,Version=v4.5,Profile=Profile7":{ - "buildOptions": { - "define": [ ] - }, - "frameworkAssemblies":{ - - } - } - } -} \ No newline at end of file -- cgit v1.2.3