aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/Services
diff options
context:
space:
mode:
authorWWWesten <4700006+WWWesten@users.noreply.github.com>2021-11-01 23:43:29 +0500
committerGitHub <noreply@github.com>2021-11-01 23:43:29 +0500
commit0a14279e2a21bcb9654a06a2d49e1e4f0cc5329c (patch)
treee1b1bd603b011ca98e5793e356326bf4a35a7050 /Emby.Server.Implementations/Services
parentf2817fef743eeb75a00782ceea363b2d3e7dc9f2 (diff)
parent76eeb8f655424d295e73ced8349c6fefee6ddb12 (diff)
Merge branch 'jellyfin:master' into master
Diffstat (limited to 'Emby.Server.Implementations/Services')
-rw-r--r--Emby.Server.Implementations/Services/HttpResult.cs61
-rw-r--r--Emby.Server.Implementations/Services/RequestHelper.cs52
-rw-r--r--Emby.Server.Implementations/Services/ResponseHelper.cs163
-rw-r--r--Emby.Server.Implementations/Services/ServiceController.cs184
-rw-r--r--Emby.Server.Implementations/Services/ServiceExec.cs219
-rw-r--r--Emby.Server.Implementations/Services/ServiceHandler.cs245
-rw-r--r--Emby.Server.Implementations/Services/ServiceMethod.cs17
-rw-r--r--Emby.Server.Implementations/Services/ServicePath.cs564
-rw-r--r--Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs128
-rw-r--r--Emby.Server.Implementations/Services/SwaggerService.cs247
-rw-r--r--Emby.Server.Implementations/Services/UrlExtensions.cs33
11 files changed, 0 insertions, 1913 deletions
diff --git a/Emby.Server.Implementations/Services/HttpResult.cs b/Emby.Server.Implementations/Services/HttpResult.cs
deleted file mode 100644
index 296da2f7a..000000000
--- a/Emby.Server.Implementations/Services/HttpResult.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using System.Collections.Generic;
-using System.IO;
-using System.Net;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Services;
-
-namespace Emby.Server.Implementations.Services
-{
- public class HttpResult
- : IHttpResult, IAsyncStreamWriter
- {
- public object Response { get; set; }
-
- public HttpResult(object response, string contentType, HttpStatusCode statusCode)
- {
- this.Headers = new Dictionary<string, string>();
-
- this.Response = response;
- this.ContentType = contentType;
- this.StatusCode = statusCode;
- }
-
- public string ContentType { get; set; }
-
- public IDictionary<string, string> Headers { get; private set; }
-
- public int Status { get; set; }
-
- public HttpStatusCode StatusCode
- {
- get => (HttpStatusCode)Status;
- set => Status = (int)value;
- }
-
- public IRequest RequestContext { get; set; }
-
- public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken)
- {
- var response = RequestContext == null ? null : RequestContext.Response;
-
- if (this.Response is byte[] bytesResponse)
- {
- var contentLength = bytesResponse.Length;
-
- if (response != null)
- {
- response.SetContentLength(contentLength);
- }
-
- if (contentLength > 0)
- {
- await responseStream.WriteAsync(bytesResponse, 0, contentLength).ConfigureAwait(false);
- }
- return;
- }
-
- await ResponseHelper.WriteObject(this.RequestContext, this.Response, response).ConfigureAwait(false);
- }
- }
-}
diff --git a/Emby.Server.Implementations/Services/RequestHelper.cs b/Emby.Server.Implementations/Services/RequestHelper.cs
deleted file mode 100644
index 2563cac99..000000000
--- a/Emby.Server.Implementations/Services/RequestHelper.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-using System;
-using System.IO;
-using System.Threading.Tasks;
-using Emby.Server.Implementations.HttpServer;
-
-namespace Emby.Server.Implementations.Services
-{
- public class RequestHelper
- {
- public static Func<Type, Stream, Task<object>> 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 host.DeserializeXml;
-
- case "application/json":
- case "text/json":
- return host.DeserializeJson;
- }
-
- return null;
- }
-
- public static Action<object, Stream> 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 host.SerializeToXml;
-
- case "application/json":
- case "text/json":
- return host.SerializeToJson;
- }
-
- return null;
- }
-
- private static string GetContentTypeWithoutEncoding(string contentType)
- {
- return contentType == null
- ? null
- : contentType.Split(';')[0].ToLowerInvariant().Trim();
- }
-
- }
-}
diff --git a/Emby.Server.Implementations/Services/ResponseHelper.cs b/Emby.Server.Implementations/Services/ResponseHelper.cs
deleted file mode 100644
index dc9975347..000000000
--- a/Emby.Server.Implementations/Services/ResponseHelper.cs
+++ /dev/null
@@ -1,163 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-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
-{
- public static class ResponseHelper
- {
- public static Task WriteToResponse(IResponse response, IRequest request, object result, CancellationToken cancellationToken)
- {
- if (result == null)
- {
- if (response.StatusCode == (int)HttpStatusCode.OK)
- {
- response.StatusCode = (int)HttpStatusCode.NoContent;
- }
-
- response.SetContentLength(0);
- return Task.CompletedTask;
- }
-
- var httpResult = result as IHttpResult;
- if (httpResult != null)
- {
- httpResult.RequestContext = request;
- request.ResponseContentType = httpResult.ContentType ?? request.ResponseContentType;
- }
-
- var defaultContentType = request.ResponseContentType;
-
- if (httpResult != null)
- {
- if (httpResult.RequestContext == null)
- httpResult.RequestContext = request;
-
- response.StatusCode = httpResult.Status;
- response.StatusDescription = httpResult.StatusCode.ToString();
- //if (string.IsNullOrEmpty(httpResult.ContentType))
- //{
- // httpResult.ContentType = defaultContentType;
- //}
- //response.ContentType = httpResult.ContentType;
- }
-
- var responseOptions = result as IHasHeaders;
- if (responseOptions != null)
- {
- foreach (var responseHeaders in responseOptions.Headers)
- {
- if (string.Equals(responseHeaders.Key, "Content-Length", StringComparison.OrdinalIgnoreCase))
- {
- response.SetContentLength(long.Parse(responseHeaders.Value));
- continue;
- }
-
- response.AddHeader(responseHeaders.Key, responseHeaders.Value);
- }
- }
-
- //ContentType='text/html' is the default for a HttpResponse
- //Do not override if another has been set
- if (response.ContentType == null || response.ContentType == "text/html")
- {
- response.ContentType = defaultContentType;
- }
-
- if (response.ContentType == "application/json")
- {
- response.ContentType += "; charset=utf-8";
- }
-
- var asyncStreamWriter = result as IAsyncStreamWriter;
- if (asyncStreamWriter != null)
- {
- return asyncStreamWriter.WriteToAsync(response.OutputStream, cancellationToken);
- }
-
- var streamWriter = result as IStreamWriter;
- if (streamWriter != null)
- {
- streamWriter.WriteTo(response.OutputStream);
- return Task.CompletedTask;
- }
-
- var fileWriter = result as FileWriter;
- if (fileWriter != null)
- {
- return fileWriter.WriteToAsync(response, cancellationToken);
- }
-
- var stream = result as Stream;
- if (stream != null)
- {
- return CopyStream(stream, response.OutputStream);
- }
-
- var bytes = result as byte[];
- if (bytes != null)
- {
- response.ContentType = "application/octet-stream";
- response.SetContentLength(bytes.Length);
-
- if (bytes.Length > 0)
- {
- return response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
- }
- return Task.CompletedTask;
- }
-
- var responseText = result as string;
- if (responseText != null)
- {
- bytes = Encoding.UTF8.GetBytes(responseText);
- response.SetContentLength(bytes.Length);
- if (bytes.Length > 0)
- {
- return response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
- }
- return Task.CompletedTask;
- }
-
- return WriteObject(request, result, response);
- }
-
- private static async Task CopyStream(Stream src, Stream dest)
- {
- using (src)
- {
- await src.CopyToAsync(dest).ConfigureAwait(false);
- }
- }
-
- public static async Task WriteObject(IRequest request, object result, IResponse response)
- {
- var contentType = request.ResponseContentType;
- var serializer = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType);
-
- using (var ms = new MemoryStream())
- {
- serializer(result, ms);
-
- ms.Position = 0;
-
- var contentLength = ms.Length;
-
- response.SetContentLength(contentLength);
-
- if (contentLength > 0)
- {
- await ms.CopyToAsync(response.OutputStream).ConfigureAwait(false);
- }
- }
-
- //serializer(result, outputStream);
- }
- }
-}
diff --git a/Emby.Server.Implementations/Services/ServiceController.cs b/Emby.Server.Implementations/Services/ServiceController.cs
deleted file mode 100644
index 5796956d8..000000000
--- a/Emby.Server.Implementations/Services/ServiceController.cs
+++ /dev/null
@@ -1,184 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Reflection;
-using System.Threading.Tasks;
-using Emby.Server.Implementations.HttpServer;
-using MediaBrowser.Model.Services;
-
-namespace Emby.Server.Implementations.Services
-{
- public delegate Task<object> InstanceExecFn(IRequest requestContext, object intance, object request);
- public delegate object ActionInvokerFn(object intance, object request);
- public delegate void VoidActionInvokerFn(object intance, object request);
-
- public class ServiceController
- {
- public static ServiceController Instance;
-
- public ServiceController()
- {
- Instance = this;
- }
-
- public void Init(HttpListenerHost appHost, Type[] serviceTypes)
- {
- foreach (var serviceType in serviceTypes)
- {
- RegisterService(appHost, serviceType);
- }
- }
-
- public void RegisterService(HttpListenerHost appHost, Type serviceType)
- {
- var processedReqs = new HashSet<Type>();
-
- var actions = ServiceExecGeneral.Reset(serviceType);
-
- foreach (var mi in serviceType.GetActions())
- {
- var requestType = mi.GetParameters()[0].ParameterType;
- if (processedReqs.Contains(requestType)) continue;
- processedReqs.Add(requestType);
-
- ServiceExecGeneral.CreateServiceRunnersFor(requestType, actions);
-
- //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(appHost, requestType, serviceType);
-
- appHost.AddServiceInfo(serviceType, requestType);
- }
- }
-
- public static Type FirstGenericType(Type type)
- {
- while (type != null)
- {
- if (type.GetTypeInfo().IsGenericType)
- return type;
-
- type = type.GetTypeInfo().BaseType;
- }
- return null;
- }
-
- public readonly RestPath.RestPathMap RestPathMap = new RestPath.RestPathMap();
-
- public void RegisterRestPaths(HttpListenerHost appHost, Type requestType, Type serviceType)
- {
- var attrs = appHost.GetRouteAttributes(requestType);
- foreach (var attr in attrs)
- {
- var restPath = new RestPath(appHost.CreateInstance, appHost.GetParseFn, requestType, serviceType, attr.Path, attr.Verbs, attr.IsHidden, attr.Summary, attr.Description);
-
- RegisterRestPath(restPath);
- }
- }
-
- private static readonly char[] InvalidRouteChars = new[] { '?', '&' };
-
- 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.GetMethodName()));
- if (restPath.Path.IndexOfAny(InvalidRouteChars) != -1)
- throw new ArgumentException(string.Format("Route '{0}' on '{1}' contains invalid chars. ", restPath.Path, restPath.RequestType.GetMethodName()));
-
- if (!RestPathMap.TryGetValue(restPath.FirstMatchHashKey, out List<RestPath> pathsAtFirstMatch))
- {
- pathsAtFirstMatch = new List<RestPath>();
- RestPathMap[restPath.FirstMatchHashKey] = pathsAtFirstMatch;
- }
- pathsAtFirstMatch.Add(restPath);
- }
-
- public RestPath GetRestPathForRequest(string httpMethod, string pathInfo)
- {
- var matchUsingPathParts = RestPath.GetPathPartsForMatching(pathInfo);
-
- List<RestPath> firstMatches;
-
- var yieldedHashMatches = RestPath.GetFirstMatchHashKeys(matchUsingPathParts);
- foreach (var potentialHashMatch in yieldedHashMatches)
- {
- if (!this.RestPathMap.TryGetValue(potentialHashMatch, out firstMatches))
- {
- continue;
- }
-
- var bestScore = -1;
- RestPath bestMatch = null;
- foreach (var restPath in firstMatches)
- {
- var score = restPath.MatchScore(httpMethod, matchUsingPathParts);
- if (score > bestScore)
- {
- bestScore = score;
- bestMatch = restPath;
- }
- }
-
- if (bestScore > 0 && bestMatch != null)
- {
- return bestMatch;
- }
- }
-
- var yieldedWildcardMatches = RestPath.GetFirstMatchWildCardHashKeys(matchUsingPathParts);
- foreach (var potentialHashMatch in yieldedWildcardMatches)
- {
- if (!this.RestPathMap.TryGetValue(potentialHashMatch, out firstMatches)) continue;
-
- var bestScore = -1;
- RestPath bestMatch = null;
- foreach (var restPath in firstMatches)
- {
- var score = restPath.MatchScore(httpMethod, matchUsingPathParts);
- if (score > bestScore)
- {
- bestScore = score;
- bestMatch = restPath;
- }
- }
-
- if (bestScore > 0 && bestMatch != null)
- {
- return bestMatch;
- }
- }
-
- return null;
- }
-
- public Task<object> Execute(HttpListenerHost appHost, object requestDto, IRequest req)
- {
- req.Dto = requestDto;
- var requestType = requestDto.GetType();
- req.OperationName = requestType.Name;
-
- var serviceType = appHost.GetServiceTypeByRequest(requestType);
-
- var service = appHost.CreateInstance(serviceType);
-
- //var service = typeFactory.CreateInstance(serviceType);
-
- var serviceRequiresContext = service as IRequiresRequest;
- if (serviceRequiresContext != null)
- {
- serviceRequiresContext.Request = req;
- }
-
- if (req.Dto == null) // Don't override existing batched DTO[]
- req.Dto = requestDto;
-
- //Executes the service and returns the result
- return ServiceExecGeneral.Execute(serviceType, req, service, requestDto, requestType.GetMethodName());
- }
- }
-
-}
diff --git a/Emby.Server.Implementations/Services/ServiceExec.cs b/Emby.Server.Implementations/Services/ServiceExec.cs
deleted file mode 100644
index 79f5c59e6..000000000
--- a/Emby.Server.Implementations/Services/ServiceExec.cs
+++ /dev/null
@@ -1,219 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Linq.Expressions;
-using System.Reflection;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Services;
-
-namespace Emby.Server.Implementations.Services
-{
- public static class ServiceExecExtensions
- {
- public static string[] AllVerbs = new[] {
- "OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT", // RFC 2616
- "PROPFIND", "PROPPATCH", "MKCOL", "COPY", "MOVE", "LOCK", "UNLOCK", // RFC 2518
- "VERSION-CONTROL", "REPORT", "CHECKOUT", "CHECKIN", "UNCHECKOUT",
- "MKWORKSPACE", "UPDATE", "LABEL", "MERGE", "BASELINE-CONTROL", "MKACTIVITY", // RFC 3253
- "ORDERPATCH", // RFC 3648
- "ACL", // RFC 3744
- "PATCH", // https://datatracker.ietf.org/doc/draft-dusseault-http-patch/
- "SEARCH", // https://datatracker.ietf.org/doc/draft-reschke-webdav-search/
- "BCOPY", "BDELETE", "BMOVE", "BPROPFIND", "BPROPPATCH", "NOTIFY",
- "POLL", "SUBSCRIBE", "UNSUBSCRIBE"
- };
-
- public static List<MethodInfo> GetActions(this Type serviceType)
- {
- var list = new List<MethodInfo>();
-
- foreach (var mi in serviceType.GetRuntimeMethods())
- {
- if (!mi.IsPublic)
- {
- continue;
- }
-
- if (mi.IsStatic)
- {
- continue;
- }
-
- if (mi.GetParameters().Length != 1)
- continue;
-
- var actionName = mi.Name;
- if (!AllVerbs.Contains(actionName, StringComparer.OrdinalIgnoreCase))
- continue;
-
- list.Add(mi);
- }
-
- return list;
- }
- }
-
- internal static class ServiceExecGeneral
- {
- private static Dictionary<string, ServiceMethod> execMap = new Dictionary<string, ServiceMethod>();
-
- public static void CreateServiceRunnersFor(Type requestType, List<ServiceMethod> actions)
- {
- foreach (var actionCtx in actions)
- {
- if (execMap.ContainsKey(actionCtx.Id)) continue;
-
- execMap[actionCtx.Id] = actionCtx;
- }
- }
-
- public static Task<object> Execute(Type serviceType, IRequest request, object instance, object requestDto, string requestName)
- {
- var actionName = request.Verb ?? "POST";
-
- if (execMap.TryGetValue(ServiceMethod.Key(serviceType, actionName, requestName), out ServiceMethod actionContext))
- {
- if (actionContext.RequestFilters != null)
- {
- foreach (var requestFilter in actionContext.RequestFilters)
- {
- requestFilter.RequestFilter(request, request.Response, requestDto);
- if (request.Response.IsClosed)
- {
- Task.FromResult<object>(null);
- }
- }
- }
-
- var response = actionContext.ServiceAction(instance, requestDto);
-
- var taskResponse = response as Task;
- if (taskResponse != null)
- {
- return GetTaskResult(taskResponse);
- }
-
- return Task.FromResult(response);
- }
-
- var expectedMethodName = actionName.Substring(0, 1) + actionName.Substring(1).ToLowerInvariant();
- throw new NotImplementedException(string.Format("Could not find method named {1}({0}) or Any({0}) on Service {2}", requestDto.GetType().GetMethodName(), expectedMethodName, serviceType.GetMethodName()));
- }
-
- private static async Task<object> GetTaskResult(Task task)
- {
- try
- {
- var taskObject = task as Task<object>;
- if (taskObject != null)
- {
- return await taskObject.ConfigureAwait(false);
- }
-
- await task.ConfigureAwait(false);
-
- var type = task.GetType().GetTypeInfo();
- if (!type.IsGenericType)
- {
- return null;
- }
-
- var resultProperty = type.GetDeclaredProperty("Result");
- if (resultProperty == null)
- {
- return null;
- }
-
- var result = resultProperty.GetValue(task);
-
- // hack alert
- if (result.GetType().Name.IndexOf("voidtaskresult", StringComparison.OrdinalIgnoreCase) != -1)
- {
- return null;
- }
-
- return result;
- }
- catch (TypeAccessException)
- {
- return null; //return null for void Task's
- }
- }
-
- public static List<ServiceMethod> Reset(Type serviceType)
- {
- var actions = new List<ServiceMethod>();
-
- foreach (var mi in serviceType.GetActions())
- {
- var actionName = mi.Name;
- var args = mi.GetParameters();
-
- var requestType = args[0].ParameterType;
- var actionCtx = new ServiceMethod
- {
- Id = ServiceMethod.Key(serviceType, actionName, requestType.GetMethodName())
- };
-
- try
- {
- actionCtx.ServiceAction = CreateExecFn(serviceType, requestType, mi);
- }
- catch
- {
- //Potential problems with MONO, using reflection for fallback
- actionCtx.ServiceAction = (service, request) =>
- mi.Invoke(service, new[] { request });
- }
-
- var reqFilters = new List<IHasRequestFilter>();
-
- foreach (var attr in mi.GetCustomAttributes(true))
- {
- var hasReqFilter = attr as IHasRequestFilter;
-
- if (hasReqFilter != null)
- reqFilters.Add(hasReqFilter);
- }
-
- if (reqFilters.Count > 0)
- actionCtx.RequestFilters = reqFilters.OrderBy(i => i.Priority).ToArray();
-
- actions.Add(actionCtx);
- }
-
- return actions;
- }
-
- private static ActionInvokerFn CreateExecFn(Type serviceType, Type requestType, MethodInfo mi)
- {
- var serviceParam = Expression.Parameter(typeof(object), "serviceObj");
- var serviceStrong = Expression.Convert(serviceParam, serviceType);
-
- var requestDtoParam = Expression.Parameter(typeof(object), "requestDto");
- var requestDtoStrong = Expression.Convert(requestDtoParam, requestType);
-
- Expression callExecute = Expression.Call(
- serviceStrong, mi, requestDtoStrong);
-
- if (mi.ReturnType != typeof(void))
- {
- var executeFunc = Expression.Lambda<ActionInvokerFn>
- (callExecute, serviceParam, requestDtoParam).Compile();
-
- return executeFunc;
- }
- else
- {
- var executeFunc = Expression.Lambda<VoidActionInvokerFn>
- (callExecute, serviceParam, requestDtoParam).Compile();
-
- return (service, request) =>
- {
- executeFunc(service, request);
- return null;
- };
- }
- }
- }
-}
diff --git a/Emby.Server.Implementations/Services/ServiceHandler.cs b/Emby.Server.Implementations/Services/ServiceHandler.cs
deleted file mode 100644
index 7e836e22c..000000000
--- a/Emby.Server.Implementations/Services/ServiceHandler.cs
+++ /dev/null
@@ -1,245 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Reflection;
-using System.Threading;
-using System.Threading.Tasks;
-using Emby.Server.Implementations.HttpServer;
-using MediaBrowser.Model.Services;
-using Microsoft.Extensions.Logging;
-
-namespace Emby.Server.Implementations.Services
-{
- public class ServiceHandler
- {
- protected static Task<object> CreateContentTypeRequest(HttpListenerHost host, IRequest httpReq, Type requestType, string contentType)
- {
- if (!string.IsNullOrEmpty(contentType) && httpReq.ContentLength > 0)
- {
- var deserializer = RequestHelper.GetRequestReader(host, contentType);
- if (deserializer != null)
- {
- return deserializer(requestType, httpReq.InputStream);
- }
- }
- return Task.FromResult(host.CreateInstance(requestType));
- }
-
- public static RestPath FindMatchingRestPath(string httpMethod, string pathInfo, out string contentType)
- {
- pathInfo = GetSanitizedPathInfo(pathInfo, out contentType);
-
- return ServiceController.Instance.GetRestPathForRequest(httpMethod, pathInfo);
- }
-
- public static string GetSanitizedPathInfo(string pathInfo, out string contentType)
- {
- contentType = null;
- var pos = pathInfo.LastIndexOf('.');
- if (pos >= 0)
- {
- var format = pathInfo.Substring(pos + 1);
- contentType = GetFormatContentType(format);
- if (contentType != null)
- {
- pathInfo = pathInfo.Substring(0, pos);
- }
- }
- return pathInfo;
- }
-
- private static string GetFormatContentType(string format)
- {
- //built-in formats
- if (format == "json")
- return "application/json";
- if (format == "xml")
- return "application/xml";
-
- return null;
- }
-
- public RestPath GetRestPath(string httpMethod, string pathInfo)
- {
- if (this.RestPath == null)
- {
- this.RestPath = FindMatchingRestPath(httpMethod, pathInfo, out string contentType);
-
- if (contentType != null)
- ResponseContentType = contentType;
- }
- return this.RestPath;
- }
-
- public RestPath RestPath { get; set; }
-
- // Set from SSHHF.GetHandlerForPathInfo()
- public string ResponseContentType { get; set; }
-
- public async Task ProcessRequestAsync(HttpListenerHost appHost, IRequest httpReq, IResponse httpRes, ILogger logger, string operationName, CancellationToken cancellationToken)
- {
- var restPath = GetRestPath(httpReq.Verb, httpReq.PathInfo);
- if (restPath == null)
- {
- throw new NotSupportedException("No RestPath found for: " + httpReq.Verb + " " + httpReq.PathInfo);
- }
-
- SetRoute(httpReq, restPath);
-
- if (ResponseContentType != null)
- httpReq.ResponseContentType = ResponseContentType;
-
- var request = httpReq.Dto = await CreateRequest(appHost, httpReq, restPath, logger).ConfigureAwait(false);
-
- appHost.ApplyRequestFilters(httpReq, httpRes, request);
-
- var response = await appHost.ServiceController.Execute(appHost, request, httpReq).ConfigureAwait(false);
-
- // Apply response filters
- foreach (var responseFilter in appHost.ResponseFilters)
- {
- responseFilter(httpReq, httpRes, response);
- }
-
- await ResponseHelper.WriteToResponse(httpRes, httpReq, response, cancellationToken).ConfigureAwait(false);
- }
-
- public static async Task<object> CreateRequest(HttpListenerHost host, IRequest httpReq, RestPath restPath, ILogger logger)
- {
- var requestType = restPath.RequestType;
-
- if (RequireqRequestStream(requestType))
- {
- // Used by IRequiresRequestStream
- var requestParams = await GetRequestParams(httpReq).ConfigureAwait(false);
- var request = ServiceHandler.CreateRequest(httpReq, restPath, requestParams, host.CreateInstance(requestType));
-
- var rawReq = (IRequiresRequestStream)request;
- rawReq.RequestStream = httpReq.InputStream;
- return rawReq;
- }
- else
- {
- var requestParams = await GetFlattenedRequestParams(httpReq).ConfigureAwait(false);
-
- var requestDto = await CreateContentTypeRequest(host, httpReq, restPath.RequestType, httpReq.ContentType).ConfigureAwait(false);
-
- return CreateRequest(httpReq, restPath, requestParams, requestDto);
- }
- }
-
- public static bool RequireqRequestStream(Type requestType)
- {
- var requiresRequestStreamTypeInfo = typeof(IRequiresRequestStream).GetTypeInfo();
-
- return requiresRequestStreamTypeInfo.IsAssignableFrom(requestType.GetTypeInfo());
- }
-
- public static object CreateRequest(IRequest httpReq, RestPath restPath, Dictionary<string, string> requestParams, object requestDto)
- {
- var pathInfo = !restPath.IsWildCardPath
- ? GetSanitizedPathInfo(httpReq.PathInfo, out string contentType)
- : httpReq.PathInfo;
-
- return restPath.CreateRequest(pathInfo, requestParams, requestDto);
- }
-
- /// <summary>
- /// Duplicate Params are given a unique key by appending a #1 suffix
- /// </summary>
- private static async Task<Dictionary<string, string>> GetRequestParams(IRequest request)
- {
- var map = new Dictionary<string, string>();
-
- foreach (var name in request.QueryString.Keys)
- {
- if (name == null) continue; //thank you ASP.NET
-
- var values = request.QueryString.GetValues(name);
- if (values.Count == 1)
- {
- map[name] = values[0];
- }
- else
- {
- for (var i = 0; i < values.Count; i++)
- {
- map[name + (i == 0 ? "" : "#" + i)] = values[i];
- }
- }
- }
-
- if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT")))
- {
- var formData = await request.GetFormData().ConfigureAwait(false);
- if (formData != null)
- {
- foreach (var name in formData.Keys)
- {
- if (name == null) continue; //thank you ASP.NET
-
- var values = formData.GetValues(name);
- if (values.Count == 1)
- {
- map[name] = values[0];
- }
- else
- {
- for (var i = 0; i < values.Count; i++)
- {
- map[name + (i == 0 ? "" : "#" + i)] = values[i];
- }
- }
- }
- }
- }
-
- return map;
- }
-
- private static bool IsMethod(string method, string expected)
- {
- return string.Equals(method, expected, StringComparison.OrdinalIgnoreCase);
- }
-
- /// <summary>
- /// Duplicate params have their values joined together in a comma-delimited string
- /// </summary>
- private static async Task<Dictionary<string, string>> GetFlattenedRequestParams(IRequest request)
- {
- var map = new Dictionary<string, string>();
-
- foreach (var name in request.QueryString.Keys)
- {
- if (name == null) continue; //thank you ASP.NET
- map[name] = request.QueryString[name];
- }
-
- if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT")))
- {
- var formData = await request.GetFormData().ConfigureAwait(false);
- if (formData != null)
- {
- foreach (var name in formData.Keys)
- {
- if (name == null) continue; //thank you ASP.NET
- map[name] = formData[name];
- }
- }
- }
-
- return map;
- }
-
- private static void SetRoute(IRequest req, RestPath route)
- {
- req.Items["__route"] = route;
- }
-
- private static RestPath GetRoute(IRequest req)
- {
- req.Items.TryGetValue("__route", out var route);
- return route as RestPath;
- }
- }
-
-}
diff --git a/Emby.Server.Implementations/Services/ServiceMethod.cs b/Emby.Server.Implementations/Services/ServiceMethod.cs
deleted file mode 100644
index 5018bf4a2..000000000
--- a/Emby.Server.Implementations/Services/ServiceMethod.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System;
-
-namespace Emby.Server.Implementations.Services
-{
- public class ServiceMethod
- {
- public string Id { get; set; }
-
- public ActionInvokerFn ServiceAction { get; set; }
- public MediaBrowser.Model.Services.IHasRequestFilter[] RequestFilters { get; set; }
-
- public static string Key(Type serviceType, string method, string requestDtoName)
- {
- return serviceType.FullName + " " + method.ToUpperInvariant() + " " + requestDtoName;
- }
- }
-}
diff --git a/Emby.Server.Implementations/Services/ServicePath.cs b/Emby.Server.Implementations/Services/ServicePath.cs
deleted file mode 100644
index f575baca3..000000000
--- a/Emby.Server.Implementations/Services/ServicePath.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;
-
-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;
- 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; }
-
- /// <summary>
- /// The number of segments separated by '/' determinable by path.Split('/').Length
- /// e.g. /path/to/here.ext == 3
- /// </summary>
- public int PathComponentsCount { get; set; }
-
- /// <summary>
- /// The total number of segments after subparts have been exploded ('.')
- /// e.g. /path/to/here.ext == 4
- /// </summary>
- public int TotalComponentsCount { get; set; }
-
- public string[] Verbs { get; private set; }
-
- public Type RequestType { get; private set; }
-
- public Type ServiceType { get; private set; }
-
- public string Path => this.restPath;
-
- public string Summary { get; private set; }
- public string Description { get; private set; }
- public bool IsHidden { get; private set; }
-
- public int Priority { get; set; } //passed back to RouteAttribute
-
- public IEnumerable<string> PathVariables => this.variablesNames.Where(e => !string.IsNullOrWhiteSpace(e));
-
- public static string[] GetPathPartsForMatching(string pathInfo)
- {
- return pathInfo.ToLowerInvariant().Split(new[] { PathSeperatorChar }, StringSplitOptions.RemoveEmptyEntries);
- }
-
- public static List<string> GetFirstMatchHashKeys(string[] pathPartsForMatching)
- {
- var hashPrefix = pathPartsForMatching.Length + PathSeperator;
- return GetPotentialMatchesWithPrefix(hashPrefix, pathPartsForMatching);
- }
-
- public static List<string> GetFirstMatchWildCardHashKeys(string[] pathPartsForMatching)
- {
- const string hashPrefix = WildCard + PathSeperator;
- return GetPotentialMatchesWithPrefix(hashPrefix, pathPartsForMatching);
- }
-
- private static List<string> GetPotentialMatchesWithPrefix(string hashPrefix, string[] pathPartsForMatching)
- {
- var list = new List<string>();
-
- 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<Type, object> createInstanceFn, Func<Type, Func<string, object>> getParseFn, Type requestType, Type serviceType, string path, string verbs, bool isHidden = false, string summary = null, string description = null)
- {
- this.RequestType = requestType;
- this.ServiceType = serviceType;
- this.Summary = summary;
- this.IsHidden = isHidden;
- this.Description = description;
- this.restPath = path;
-
- this.Verbs = string.IsNullOrWhiteSpace(verbs) ? ServiceExecExtensions.AllVerbs : verbs.ToUpperInvariant().Split(new[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries);
-
- var componentsList = new List<string>();
-
- //We only split on '.' if the restPath has them. Allows for /{action}.{type}
- var hasSeparators = new List<bool>();
- 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;
-
- 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.ToLowerInvariant();
-
- 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.typeDeserializer = new StringMapTypeDeserializer(createInstanceFn, getParseFn, this.RequestType);
- RegisterCaseInsenstivePropertyNameMappings();
- }
-
- private void RegisterCaseInsenstivePropertyNameMappings()
- {
- foreach (var propertyInfo in GetSerializableProperties(RequestType))
- {
- var propertyName = propertyInfo.Name;
- propertyNamesMap.Add(propertyName.ToLowerInvariant(), propertyName);
- }
- }
-
- internal static string[] IgnoreAttributesNamed = new[] {
- "IgnoreDataMemberAttribute",
- "JsonIgnoreAttribute"
- };
-
-
- private static Type excludeType = typeof(Stream);
-
- internal static List<PropertyInfo> GetSerializableProperties(Type type)
- {
- var list = new List<PropertyInfo>();
- var props = GetPublicProperties(type);
-
- foreach (var prop in props)
- {
- if (prop.GetMethod == null)
- {
- continue;
- }
-
- if (excludeType == prop.PropertyType)
- {
- continue;
- }
-
- var ignored = false;
- foreach (var attr in prop.GetCustomAttributes(true))
- {
- if (IgnoreAttributesNamed.Contains(attr.GetType().Name))
- {
- ignored = true;
- break;
- }
- }
-
- if (!ignored)
- {
- list.Add(prop);
- }
- }
-
- // else return those properties that are not decorated with IgnoreDataMember
- return list;
- }
-
- private static List<PropertyInfo> GetPublicProperties(Type type)
- {
- if (type.GetTypeInfo().IsInterface)
- {
- var propertyInfos = new List<PropertyInfo>();
-
- var considered = new List<Type>();
- var queue = new Queue<Type>();
- 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;
- }
-
- var list = new List<PropertyInfo>();
-
- foreach (var t in GetTypesPublicProperties(type))
- {
- if (t.GetIndexParameters().Length == 0)
- {
- list.Add(t);
- }
- }
- return list;
- }
-
- private static PropertyInfo[] GetTypesPublicProperties(Type subType)
- {
- var pis = new List<PropertyInfo>();
- foreach (var pi in subType.GetRuntimeProperties())
- {
- var mi = pi.GetMethod ?? pi.SetMethod;
- if (mi != null && mi.IsStatic) continue;
- pis.Add(pi);
- }
- return pis.ToArray();
- }
-
- /// <summary>
- /// Provide for quick lookups based on hashes that can be determined from a request url
- /// </summary>
- public string FirstMatchHashKey { get; private set; }
-
- private readonly StringMapTypeDeserializer typeDeserializer;
-
- private readonly Dictionary<string, string> propertyNamesMap = new Dictionary<string, string>();
-
- public int MatchScore(string httpMethod, string[] withPathInfoParts)
- {
- var isMatch = IsMatch(httpMethod, withPathInfoParts, out var 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
- if (Verbs.Length == 1 && string.Equals(httpMethod, Verbs[0], StringComparison.OrdinalIgnoreCase))
- {
- score += 10;
- }
- else
- {
- score += 1;
- }
-
- return score;
- }
-
- private bool StringContains(string str1, string str2)
- {
- return str1.IndexOf(str2, StringComparison.OrdinalIgnoreCase) != -1;
- }
-
- /// <summary>
- /// For performance withPathInfoParts should already be a lower case string
- /// to minimize redundant matching operations.
- /// </summary>
- public bool IsMatch(string httpMethod, string[] withPathInfoParts, out int wildcardMatchCount)
- {
- wildcardMatchCount = 0;
-
- if (withPathInfoParts.Length != this.PathComponentsCount && !this.IsWildCardPath)
- {
- return false;
- }
-
- if (!Verbs.Contains(httpMethod, StringComparer.OrdinalIgnoreCase))
- {
- return false;
- }
-
- if (!ExplodeComponents(ref withPathInfoParts))
- {
- return false;
- }
-
- if (this.TotalComponentsCount != withPathInfoParts.Length && !this.IsWildCardPath)
- {
- 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))
- {
- 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))
- {
- return false;
- }
- pathIx++;
- }
- }
-
- return pathIx == withPathInfoParts.Length;
- }
-
- private static 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<string>();
- 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<string, string> queryStringAndFormData, object fromInstance)
- {
- var requestComponents = pathInfo.Split(new[] { PathSeperatorChar }, StringSplitOptions.RemoveEmptyEntries);
-
- 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<string, string>();
- var pathIx = 0;
- for (var i = 0; i < this.TotalComponentsCount; i++)
- {
- var variableName = this.variablesNames[i];
- if (variableName == null)
- {
- pathIx++;
- continue;
- }
-
- if (!this.propertyNamesMap.TryGetValue(variableName.ToLowerInvariant(), out var 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 class RestPathMap : SortedDictionary<string, List<RestPath>>
- {
- public RestPathMap() : base(StringComparer.OrdinalIgnoreCase)
- {
- }
- }
- }
-}
diff --git a/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs b/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs
deleted file mode 100644
index d13935fba..000000000
--- a/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs
+++ /dev/null
@@ -1,128 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Reflection;
-
-namespace Emby.Server.Implementations.Services
-{
- /// <summary>
- /// Serializer cache of delegates required to create a type from a string map (e.g. for REST urls)
- /// </summary>
- public class StringMapTypeDeserializer
- {
- internal class PropertySerializerEntry
- {
- public PropertySerializerEntry(Action<object, object> propertySetFn, Func<string, object> propertyParseStringFn)
- {
- PropertySetFn = propertySetFn;
- PropertyParseStringFn = propertyParseStringFn;
- }
-
- public Action<object, object> PropertySetFn;
- public Func<string, object> PropertyParseStringFn;
- public Type PropertyType;
- }
-
- private readonly Type type;
- private readonly Dictionary<string, PropertySerializerEntry> propertySetterMap
- = new Dictionary<string, PropertySerializerEntry>(StringComparer.OrdinalIgnoreCase);
-
- public Func<string, object> GetParseFn(Type propertyType)
- {
- if (propertyType == typeof(string))
- return s => s;
-
- return _GetParseFn(propertyType);
- }
-
- private readonly Func<Type, object> _CreateInstanceFn;
- private readonly Func<Type, Func<string, object>> _GetParseFn;
-
- public StringMapTypeDeserializer(Func<Type, object> createInstanceFn, Func<Type, Func<string, object>> 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<string, string> keyValuePairs)
- {
- string propertyName = null;
- string propertyTextValue = null;
- PropertySerializerEntry propertySerializerEntry = null;
-
- if (instance == null)
- instance = _CreateInstanceFn(type);
-
- foreach (var pair in keyValuePairs)
- {
- propertyName = pair.Key;
- propertyTextValue = pair.Value;
-
- if (string.IsNullOrEmpty(propertyTextValue))
- {
- continue;
- }
-
- 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<object, object> GetSetPropertyMethod(Type type, PropertyInfo propertyInfo)
- {
- if (!propertyInfo.CanWrite || propertyInfo.GetIndexParameters().Length > 0) return null;
-
- var setMethodInfo = propertyInfo.SetMethod;
- return (instance, value) => setMethodInfo.Invoke(instance, new[] { value });
- }
- }
-}
diff --git a/Emby.Server.Implementations/Services/SwaggerService.cs b/Emby.Server.Implementations/Services/SwaggerService.cs
deleted file mode 100644
index 3e6970eef..000000000
--- a/Emby.Server.Implementations/Services/SwaggerService.cs
+++ /dev/null
@@ -1,247 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Services;
-
-namespace Emby.Server.Implementations.Services
-{
- [Route("/swagger", "GET", Summary = "Gets the swagger specifications")]
- [Route("/swagger.json", "GET", Summary = "Gets the swagger specifications")]
- public class GetSwaggerSpec : IReturn<SwaggerSpec>
- {
- }
-
- public class SwaggerSpec
- {
- public string swagger { get; set; }
- public string[] schemes { get; set; }
- public SwaggerInfo info { get; set; }
- public string host { get; set; }
- public string basePath { get; set; }
- public SwaggerTag[] tags { get; set; }
- public IDictionary<string, Dictionary<string, SwaggerMethod>> paths { get; set; }
- public Dictionary<string, SwaggerDefinition> definitions { get; set; }
- public SwaggerComponents components { get; set; }
- }
-
- public class SwaggerComponents
- {
- public Dictionary<string, SwaggerSecurityScheme> securitySchemes { get; set; }
- }
-
- public class SwaggerSecurityScheme
- {
- public string name { get; set; }
- public string type { get; set; }
- public string @in { get; set; }
- }
-
- public class SwaggerInfo
- {
- public string description { get; set; }
- public string version { get; set; }
- public string title { get; set; }
- public string termsOfService { get; set; }
-
- public SwaggerConcactInfo contact { get; set; }
- }
-
- public class SwaggerConcactInfo
- {
- public string email { get; set; }
- public string name { get; set; }
- public string url { get; set; }
- }
-
- public class SwaggerTag
- {
- public string description { get; set; }
- public string name { get; set; }
- }
-
- public class SwaggerMethod
- {
- public string summary { get; set; }
- public string description { get; set; }
- public string[] tags { get; set; }
- public string operationId { get; set; }
- public string[] consumes { get; set; }
- public string[] produces { get; set; }
- public SwaggerParam[] parameters { get; set; }
- public Dictionary<string, SwaggerResponse> responses { get; set; }
- public Dictionary<string, string[]>[] security { get; set; }
- }
-
- public class SwaggerParam
- {
- public string @in { get; set; }
- public string name { get; set; }
- public string description { get; set; }
- public bool required { get; set; }
- public string type { get; set; }
- public string collectionFormat { get; set; }
- }
-
- public class SwaggerResponse
- {
- public string description { get; set; }
-
- // ex. "$ref":"#/definitions/Pet"
- public Dictionary<string, string> schema { get; set; }
- }
-
- public class SwaggerDefinition
- {
- public string type { get; set; }
- public Dictionary<string, SwaggerProperty> properties { get; set; }
- }
-
- public class SwaggerProperty
- {
- public string type { get; set; }
- public string format { get; set; }
- public string description { get; set; }
- public string[] @enum { get; set; }
- public string @default { get; set; }
- }
-
- public class SwaggerService : IService, IRequiresRequest
- {
- private SwaggerSpec _spec;
-
- public IRequest Request { get; set; }
-
- public object Get(GetSwaggerSpec request)
- {
- return _spec ?? (_spec = GetSpec());
- }
-
- private SwaggerSpec GetSpec()
- {
- string host = null;
- Uri uri;
- if (Uri.TryCreate(Request.RawUrl, UriKind.Absolute, out uri))
- {
- host = uri.Host;
- }
-
- var securitySchemes = new Dictionary<string, SwaggerSecurityScheme>();
-
- securitySchemes["api_key"] = new SwaggerSecurityScheme
- {
- name = "api_key",
- type = "apiKey",
- @in = "query"
- };
-
- var spec = new SwaggerSpec
- {
- schemes = new[] { "http" },
- tags = GetTags(),
- swagger = "2.0",
- info = new SwaggerInfo
- {
- title = "Jellyfin Server API",
- version = "1.0.0",
- description = "Explore the Jellyfin Server API",
- contact = new SwaggerConcactInfo
- {
- name = "Jellyfin Community",
- url = "https://jellyfin.readthedocs.io/en/latest/user-docs/getting-help/"
- }
- },
- paths = GetPaths(),
- definitions = GetDefinitions(),
- basePath = "/jellyfin",
- host = host,
-
- components = new SwaggerComponents
- {
- securitySchemes = securitySchemes
- }
- };
-
- return spec;
- }
-
-
- private SwaggerTag[] GetTags()
- {
- return new SwaggerTag[] { };
- }
-
- private Dictionary<string, SwaggerDefinition> GetDefinitions()
- {
- return new Dictionary<string, SwaggerDefinition>();
- }
-
- private IDictionary<string, Dictionary<string, SwaggerMethod>> GetPaths()
- {
- var paths = new SortedDictionary<string, Dictionary<string, SwaggerMethod>>();
-
- var all = ServiceController.Instance.RestPathMap.OrderBy(i => i.Key, StringComparer.OrdinalIgnoreCase).ToList();
-
- foreach (var current in all)
- {
- foreach (var info in current.Value)
- {
- if (info.IsHidden)
- {
- continue;
- }
-
- if (info.Path.StartsWith("/mediabrowser", StringComparison.OrdinalIgnoreCase))
- {
- continue;
- }
- if (info.Path.StartsWith("/jellyfin", StringComparison.OrdinalIgnoreCase))
- {
- continue;
- }
-
- paths[info.Path] = GetPathInfo(info);
- }
- }
-
- return paths;
- }
-
- private Dictionary<string, SwaggerMethod> GetPathInfo(RestPath info)
- {
- var result = new Dictionary<string, SwaggerMethod>();
-
- foreach (var verb in info.Verbs)
- {
- var responses = new Dictionary<string, SwaggerResponse>
- {
- { "200", new SwaggerResponse { description = "OK" } }
- };
-
- var apiKeySecurity = new Dictionary<string, string[]>
- {
- { "api_key", Array.Empty<string>() }
- };
-
- result[verb.ToLowerInvariant()] = new SwaggerMethod
- {
- summary = info.Summary,
- description = info.Description,
- produces = new[] { "application/json" },
- consumes = new[] { "application/json" },
- operationId = info.RequestType.Name,
- tags = Array.Empty<string>(),
-
- parameters = Array.Empty<SwaggerParam>(),
-
- responses = responses,
-
- security = new [] { apiKeySecurity }
- };
- }
-
- return result;
- }
- }
-}
diff --git a/Emby.Server.Implementations/Services/UrlExtensions.cs b/Emby.Server.Implementations/Services/UrlExtensions.cs
deleted file mode 100644
index 8899fbfa3..000000000
--- a/Emby.Server.Implementations/Services/UrlExtensions.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using System;
-
-namespace Emby.Server.Implementations.Services
-{
- /// <summary>
- /// 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
- /// </summary>
- 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;
- }
-
- private 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);
- }
- }
-}