aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/Services
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations/Services')
-rw-r--r--Emby.Server.Implementations/Services/HttpResult.cs14
-rw-r--r--Emby.Server.Implementations/Services/RequestHelper.cs4
-rw-r--r--Emby.Server.Implementations/Services/ServiceController.cs6
-rw-r--r--Emby.Server.Implementations/Services/ServiceExec.cs4
-rw-r--r--Emby.Server.Implementations/Services/ServiceHandler.cs13
-rw-r--r--Emby.Server.Implementations/Services/ServiceMethod.cs4
-rw-r--r--Emby.Server.Implementations/Services/ServicePath.cs37
-rw-r--r--Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs4
-rw-r--r--Emby.Server.Implementations/Services/SwaggerService.cs259
-rw-r--r--Emby.Server.Implementations/Services/UrlExtensions.cs6
10 files changed, 297 insertions, 54 deletions
diff --git a/Emby.Server.Implementations/Services/HttpResult.cs b/Emby.Server.Implementations/Services/HttpResult.cs
index 91314c15a..296da2f7a 100644
--- a/Emby.Server.Implementations/Services/HttpResult.cs
+++ b/Emby.Server.Implementations/Services/HttpResult.cs
@@ -15,7 +15,6 @@ namespace Emby.Server.Implementations.Services
public HttpResult(object response, string contentType, HttpStatusCode statusCode)
{
this.Headers = new Dictionary<string, string>();
- this.Cookies = new List<Cookie>();
this.Response = response;
this.ContentType = contentType;
@@ -26,29 +25,28 @@ namespace Emby.Server.Implementations.Services
public IDictionary<string, string> Headers { get; private set; }
- public List<Cookie> Cookies { get; private set; }
-
public int Status { get; set; }
public HttpStatusCode StatusCode
{
- get { return (HttpStatusCode)Status; }
- set { Status = (int)value; }
+ get => (HttpStatusCode)Status;
+ set => Status = (int)value;
}
public IRequest RequestContext { get; set; }
public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken)
{
- var response = RequestContext != null ? RequestContext.Response : null;
+ var response = RequestContext == null ? null : RequestContext.Response;
- var bytesResponse = this.Response as byte[];
- if (bytesResponse != null)
+ if (this.Response is byte[] bytesResponse)
{
var contentLength = bytesResponse.Length;
if (response != null)
+ {
response.SetContentLength(contentLength);
+ }
if (contentLength > 0)
{
diff --git a/Emby.Server.Implementations/Services/RequestHelper.cs b/Emby.Server.Implementations/Services/RequestHelper.cs
index 711ba8bbc..24e9cbfa4 100644
--- a/Emby.Server.Implementations/Services/RequestHelper.cs
+++ b/Emby.Server.Implementations/Services/RequestHelper.cs
@@ -1,7 +1,7 @@
using System;
using System.IO;
-using Emby.Server.Implementations.HttpServer;
using System.Threading.Tasks;
+using Emby.Server.Implementations.HttpServer;
namespace Emby.Server.Implementations.Services
{
@@ -49,4 +49,4 @@ namespace Emby.Server.Implementations.Services
}
}
-} \ No newline at end of file
+}
diff --git a/Emby.Server.Implementations/Services/ServiceController.cs b/Emby.Server.Implementations/Services/ServiceController.cs
index 46af83128..5796956d8 100644
--- a/Emby.Server.Implementations/Services/ServiceController.cs
+++ b/Emby.Server.Implementations/Services/ServiceController.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
using Emby.Server.Implementations.HttpServer;
-using Microsoft.Extensions.Logging;
using MediaBrowser.Model.Services;
namespace Emby.Server.Implementations.Services
@@ -73,7 +72,7 @@ namespace Emby.Server.Implementations.Services
public void RegisterRestPaths(HttpListenerHost appHost, Type requestType, Type serviceType)
{
var attrs = appHost.GetRouteAttributes(requestType);
- foreach (RouteAttribute attr in attrs)
+ 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);
@@ -90,8 +89,7 @@ namespace Emby.Server.Implementations.Services
if (restPath.Path.IndexOfAny(InvalidRouteChars) != -1)
throw new ArgumentException(string.Format("Route '{0}' on '{1}' contains invalid chars. ", restPath.Path, restPath.RequestType.GetMethodName()));
- List<RestPath> pathsAtFirstMatch;
- if (!RestPathMap.TryGetValue(restPath.FirstMatchHashKey, out pathsAtFirstMatch))
+ if (!RestPathMap.TryGetValue(restPath.FirstMatchHashKey, out List<RestPath> pathsAtFirstMatch))
{
pathsAtFirstMatch = new List<RestPath>();
RestPathMap[restPath.FirstMatchHashKey] = pathsAtFirstMatch;
diff --git a/Emby.Server.Implementations/Services/ServiceExec.cs b/Emby.Server.Implementations/Services/ServiceExec.cs
index 9516c1e38..45c918fa1 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 MediaBrowser.Model.Extensions;
namespace Emby.Server.Implementations.Services
{
@@ -74,8 +73,7 @@ namespace Emby.Server.Implementations.Services
{
var actionName = request.Verb ?? "POST";
- ServiceMethod actionContext;
- if (ServiceExecGeneral.execMap.TryGetValue(ServiceMethod.Key(serviceType, actionName, requestName), out actionContext))
+ if (execMap.TryGetValue(ServiceMethod.Key(serviceType, actionName, requestName), out ServiceMethod actionContext))
{
if (actionContext.RequestFilters != null)
{
diff --git a/Emby.Server.Implementations/Services/ServiceHandler.cs b/Emby.Server.Implementations/Services/ServiceHandler.cs
index f5fcb5fe6..7e836e22c 100644
--- a/Emby.Server.Implementations/Services/ServiceHandler.cs
+++ b/Emby.Server.Implementations/Services/ServiceHandler.cs
@@ -4,8 +4,8 @@ using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Emby.Server.Implementations.HttpServer;
-using Microsoft.Extensions.Logging;
using MediaBrowser.Model.Services;
+using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Services
{
@@ -21,7 +21,7 @@ namespace Emby.Server.Implementations.Services
return deserializer(requestType, httpReq.InputStream);
}
}
- return Task.FromResult(host.CreateInstance(requestType));
+ return Task.FromResult(host.CreateInstance(requestType));
}
public static RestPath FindMatchingRestPath(string httpMethod, string pathInfo, out string contentType)
@@ -62,8 +62,7 @@ namespace Emby.Server.Implementations.Services
{
if (this.RestPath == null)
{
- string contentType;
- this.RestPath = FindMatchingRestPath(httpMethod, pathInfo, out contentType);
+ this.RestPath = FindMatchingRestPath(httpMethod, pathInfo, out string contentType);
if (contentType != null)
ResponseContentType = contentType;
@@ -137,9 +136,8 @@ namespace Emby.Server.Implementations.Services
public static object CreateRequest(IRequest httpReq, RestPath restPath, Dictionary<string, string> requestParams, object requestDto)
{
- string contentType;
var pathInfo = !restPath.IsWildCardPath
- ? GetSanitizedPathInfo(httpReq.PathInfo, out contentType)
+ ? GetSanitizedPathInfo(httpReq.PathInfo, out string contentType)
: httpReq.PathInfo;
return restPath.CreateRequest(pathInfo, requestParams, requestDto);
@@ -239,8 +237,7 @@ namespace Emby.Server.Implementations.Services
private static RestPath GetRoute(IRequest req)
{
- object route;
- req.Items.TryGetValue("__route", out route);
+ 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
index fa2dd43d0..7add72815 100644
--- a/Emby.Server.Implementations/Services/ServiceMethod.cs
+++ b/Emby.Server.Implementations/Services/ServiceMethod.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
namespace Emby.Server.Implementations.Services
{
@@ -14,4 +14,4 @@ namespace Emby.Server.Implementations.Services
return serviceType.FullName + " " + method.ToUpper() + " " + requestDtoName;
}
}
-} \ No newline at end of file
+}
diff --git a/Emby.Server.Implementations/Services/ServicePath.cs b/Emby.Server.Implementations/Services/ServicePath.cs
index ac2af3eaf..7e1993b71 100644
--- a/Emby.Server.Implementations/Services/ServicePath.cs
+++ b/Emby.Server.Implementations/Services/ServicePath.cs
@@ -4,8 +4,6 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
-using Microsoft.Extensions.Logging;
-using MediaBrowser.Model.Extensions;
namespace Emby.Server.Implementations.Services
{
@@ -39,7 +37,7 @@ namespace Emby.Server.Implementations.Services
public int PathComponentsCount { get; set; }
/// <summary>
- /// The total number of segments after subparts have been exploded ('.')
+ /// The total number of segments after subparts have been exploded ('.')
/// e.g. /path/to/here.ext == 4
/// </summary>
public int TotalComponentsCount { get; set; }
@@ -50,7 +48,7 @@ namespace Emby.Server.Implementations.Services
public Type ServiceType { get; private set; }
- public string Path { get { return this.restPath; } }
+ public string Path => this.restPath;
public string Summary { get; private set; }
public string Description { get; private set; }
@@ -58,10 +56,7 @@ namespace Emby.Server.Implementations.Services
public int Priority { get; set; } //passed back to RouteAttribute
- public IEnumerable<string> PathVariables
- {
- get { return this.variablesNames.Where(e => !string.IsNullOrWhiteSpace(e)); }
- }
+ public IEnumerable<string> PathVariables => this.variablesNames.Where(e => !string.IsNullOrWhiteSpace(e));
public static string[] GetPathPartsForMatching(string pathInfo)
{
@@ -117,7 +112,7 @@ namespace Emby.Server.Implementations.Services
var hasSeparators = new List<bool>();
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)
@@ -311,8 +306,7 @@ namespace Emby.Server.Implementations.Services
public int MatchScore(string httpMethod, string[] withPathInfoParts)
{
- int wildcardMatchCount;
- var isMatch = IsMatch(httpMethod, withPathInfoParts, out wildcardMatchCount);
+ var isMatch = IsMatch(httpMethod, withPathInfoParts, out var wildcardMatchCount);
if (!isMatch)
{
return -1;
@@ -354,7 +348,7 @@ namespace Emby.Server.Implementations.Services
if (withPathInfoParts.Length != this.PathComponentsCount && !this.IsWildCardPath)
{
- return false;
+ return false;
}
if (!Verbs.Contains(httpMethod, StringComparer.OrdinalIgnoreCase))
@@ -420,10 +414,10 @@ namespace Emby.Server.Implementations.Services
return pathIx == withPathInfoParts.Length;
}
- private bool LiteralsEqual(string str1, string str2)
+ private static bool LiteralsEqual(string str1, string str2)
{
// Most cases
- if (String.Equals(str1, str2, StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(str1, str2, StringComparison.OrdinalIgnoreCase))
{
return true;
}
@@ -433,7 +427,7 @@ namespace Emby.Server.Implementations.Services
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)
@@ -442,7 +436,7 @@ namespace Emby.Server.Implementations.Services
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])
@@ -473,7 +467,7 @@ namespace Emby.Server.Implementations.Services
&& 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));
}
@@ -489,10 +483,9 @@ namespace Emby.Server.Implementations.Services
continue;
}
- string propertyNameOnRequest;
- if (!this.propertyNamesMap.TryGetValue(variableName.ToLower(), out propertyNameOnRequest))
+ if (!this.propertyNamesMap.TryGetValue(variableName.ToLower(), out var propertyNameOnRequest))
{
- if (String.Equals("ignore", variableName, StringComparison.OrdinalIgnoreCase))
+ if (string.Equals("ignore", variableName, StringComparison.OrdinalIgnoreCase))
{
pathIx++;
continue;
@@ -522,12 +515,12 @@ namespace Emby.Server.Implementations.Services
// 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/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs b/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs
index 2233bf918..d13935fba 100644
--- a/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs
+++ b/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs
@@ -11,14 +11,14 @@ namespace Emby.Server.Implementations.Services
{
internal class PropertySerializerEntry
{
- public PropertySerializerEntry(Action<object,object> propertySetFn, Func<string, object> propertyParseStringFn)
+ 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 Func<string, object> PropertyParseStringFn;
public Type PropertyType;
}
diff --git a/Emby.Server.Implementations/Services/SwaggerService.cs b/Emby.Server.Implementations/Services/SwaggerService.cs
new file mode 100644
index 000000000..9bceeabec
--- /dev/null
+++ b/Emby.Server.Implementations/Services/SwaggerService.cs
@@ -0,0 +1,259 @@
+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>
+ {
+ };
+
+ responses["200"] = new SwaggerResponse
+ {
+ description = "OK"
+ };
+
+ var security = new List<Dictionary<string, string[]>>();
+
+ var apiKeySecurity = new Dictionary<string, string[]>();
+ apiKeySecurity["api_key"] = Array.Empty<string>();
+
+ security.Add(apiKeySecurity);
+
+ result[verb.ToLower()] = 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 = new SwaggerParam[] { },
+
+ responses = responses,
+
+ security = security.ToArray()
+ };
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/Services/UrlExtensions.cs b/Emby.Server.Implementations/Services/UrlExtensions.cs
index ba9889c41..8899fbfa3 100644
--- a/Emby.Server.Implementations/Services/UrlExtensions.cs
+++ b/Emby.Server.Implementations/Services/UrlExtensions.cs
@@ -1,11 +1,11 @@
-using System;
+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
@@ -30,4 +30,4 @@ namespace Emby.Server.Implementations.Services
: strVal.Substring(0, pos);
}
}
-} \ No newline at end of file
+}