Browse Source

Remove ServiceStack and related stuff

Claus Vium 4 years ago
parent
commit
e337756428
45 changed files with 91 additions and 4649 deletions
  1. 1 7
      Emby.Server.Implementations/ApplicationHost.cs
  2. 0 250
      Emby.Server.Implementations/HttpServer/FileWriter.cs
  3. 22 211
      Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
  4. 0 721
      Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
  5. 0 212
      Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs
  6. 0 113
      Emby.Server.Implementations/HttpServer/ResponseFilter.cs
  7. 1 212
      Emby.Server.Implementations/HttpServer/Security/AuthService.cs
  8. 10 11
      Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
  9. 7 13
      Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
  10. 0 120
      Emby.Server.Implementations/HttpServer/StreamWriter.cs
  11. 0 64
      Emby.Server.Implementations/Services/HttpResult.cs
  12. 0 51
      Emby.Server.Implementations/Services/RequestHelper.cs
  13. 0 141
      Emby.Server.Implementations/Services/ResponseHelper.cs
  14. 0 202
      Emby.Server.Implementations/Services/ServiceController.cs
  15. 0 230
      Emby.Server.Implementations/Services/ServiceExec.cs
  16. 0 212
      Emby.Server.Implementations/Services/ServiceHandler.cs
  17. 0 20
      Emby.Server.Implementations/Services/ServiceMethod.cs
  18. 0 550
      Emby.Server.Implementations/Services/ServicePath.cs
  19. 0 118
      Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs
  20. 0 27
      Emby.Server.Implementations/Services/UrlExtensions.cs
  21. 0 248
      Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs
  22. 42 13
      MediaBrowser.Common/Extensions/HttpContextExtensions.cs
  23. 0 30
      MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
  24. 0 76
      MediaBrowser.Controller/Net/AuthenticatedAttribute.cs
  25. 0 17
      MediaBrowser.Controller/Net/IAuthService.cs
  26. 1 2
      MediaBrowser.Controller/Net/IAuthorizationContext.cs
  27. 0 17
      MediaBrowser.Controller/Net/IHasResultFactory.cs
  28. 0 82
      MediaBrowser.Controller/Net/IHttpResultFactory.cs
  29. 4 5
      MediaBrowser.Controller/Net/IHttpServer.cs
  30. 3 3
      MediaBrowser.Controller/Net/ISessionContext.cs
  31. 0 44
      MediaBrowser.Controller/Net/StaticResultOptions.cs
  32. 0 65
      MediaBrowser.Model/Services/ApiMemberAttribute.cs
  33. 0 13
      MediaBrowser.Model/Services/IAsyncStreamWriter.cs
  34. 0 11
      MediaBrowser.Model/Services/IHasHeaders.cs
  35. 0 24
      MediaBrowser.Model/Services/IHasRequestFilter.cs
  36. 0 17
      MediaBrowser.Model/Services/IHttpRequest.cs
  37. 0 35
      MediaBrowser.Model/Services/IHttpResult.cs
  38. 0 93
      MediaBrowser.Model/Services/IRequest.cs
  39. 0 14
      MediaBrowser.Model/Services/IRequiresRequestStream.cs
  40. 0 15
      MediaBrowser.Model/Services/IService.cs
  41. 0 11
      MediaBrowser.Model/Services/IStreamWriter.cs
  42. 0 147
      MediaBrowser.Model/Services/QueryParamCollection.cs
  43. 0 163
      MediaBrowser.Model/Services/RouteAttribute.cs
  44. 0 1
      MediaBrowser.Model/Session/PlayRequest.cs
  45. 0 18
      tests/Jellyfin.Server.Implementations.Tests/HttpServer/ResponseFilterTests.cs

+ 1 - 7
Emby.Server.Implementations/ApplicationHost.cs

@@ -41,7 +41,6 @@ using Emby.Server.Implementations.QuickConnect;
 using Emby.Server.Implementations.ScheduledTasks;
 using Emby.Server.Implementations.Security;
 using Emby.Server.Implementations.Serialization;
-using Emby.Server.Implementations.Services;
 using Emby.Server.Implementations.Session;
 using Emby.Server.Implementations.SyncPlay;
 using Emby.Server.Implementations.TV;
@@ -90,7 +89,6 @@ using MediaBrowser.Model.IO;
 using MediaBrowser.Model.MediaInfo;
 using MediaBrowser.Model.Net;
 using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Services;
 using MediaBrowser.Model.System;
 using MediaBrowser.Model.Tasks;
 using MediaBrowser.Providers.Chapters;
@@ -544,8 +542,6 @@ namespace Emby.Server.Implementations
 
             ServiceCollection.AddSingleton<IZipClient, ZipClient>();
 
-            ServiceCollection.AddSingleton<IHttpResultFactory, HttpResultFactory>();
-
             ServiceCollection.AddSingleton<IServerApplicationHost>(this);
             ServiceCollection.AddSingleton<IServerApplicationPaths>(ApplicationPaths);
 
@@ -581,7 +577,6 @@ namespace Emby.Server.Implementations
 
             ServiceCollection.AddSingleton<ISearchEngine, SearchEngine>();
 
-            ServiceCollection.AddSingleton<ServiceController>();
             ServiceCollection.AddSingleton<IHttpServer, HttpListenerHost>();
 
             ServiceCollection.AddSingleton<IImageProcessor, ImageProcessor>();
@@ -757,7 +752,6 @@ namespace Emby.Server.Implementations
             CollectionFolder.XmlSerializer = _xmlSerializer;
             CollectionFolder.JsonSerializer = Resolve<IJsonSerializer>();
             CollectionFolder.ApplicationHost = this;
-            AuthenticatedAttribute.AuthService = Resolve<IAuthService>();
         }
 
         /// <summary>
@@ -777,7 +771,7 @@ namespace Emby.Server.Implementations
                         .Where(i => i != null)
                         .ToArray();
 
-            _httpServer.Init(GetExportTypes<IService>(), GetExports<IWebSocketListener>(), GetUrlPrefixes());
+            _httpServer.Init(GetExports<IWebSocketListener>(), GetUrlPrefixes());
 
             Resolve<ILibraryManager>().AddParts(
                 GetExports<IResolverIgnoreRule>(),

+ 0 - 250
Emby.Server.Implementations/HttpServer/FileWriter.cs

@@ -1,250 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Net;
-using System.Runtime.InteropServices;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Services;
-using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Logging;
-using Microsoft.Net.Http.Headers;
-
-namespace Emby.Server.Implementations.HttpServer
-{
-    public class FileWriter : IHttpResult
-    {
-        private static readonly CultureInfo UsCulture = CultureInfo.ReadOnly(new CultureInfo("en-US"));
-
-        private static readonly string[] _skipLogExtensions = {
-            ".js",
-            ".html",
-            ".css"
-        };
-
-        private readonly IStreamHelper _streamHelper;
-        private readonly ILogger _logger;
-
-        /// <summary>
-        /// The _options.
-        /// </summary>
-        private readonly IDictionary<string, string> _options = new Dictionary<string, string>();
-
-        /// <summary>
-        /// The _requested ranges.
-        /// </summary>
-        private List<KeyValuePair<long, long?>> _requestedRanges;
-
-        public FileWriter(string path, string contentType, string rangeHeader, ILogger logger, IFileSystem fileSystem, IStreamHelper streamHelper)
-        {
-            if (string.IsNullOrEmpty(contentType))
-            {
-                throw new ArgumentNullException(nameof(contentType));
-            }
-
-            _streamHelper = streamHelper;
-
-            Path = path;
-            _logger = logger;
-            RangeHeader = rangeHeader;
-
-            Headers[HeaderNames.ContentType] = contentType;
-
-            TotalContentLength = fileSystem.GetFileInfo(path).Length;
-            Headers[HeaderNames.AcceptRanges] = "bytes";
-
-            if (string.IsNullOrWhiteSpace(rangeHeader))
-            {
-                Headers[HeaderNames.ContentLength] = TotalContentLength.ToString(CultureInfo.InvariantCulture);
-                StatusCode = HttpStatusCode.OK;
-            }
-            else
-            {
-                StatusCode = HttpStatusCode.PartialContent;
-                SetRangeValues();
-            }
-
-            FileShare = FileShare.Read;
-            Cookies = new List<Cookie>();
-        }
-
-        private string RangeHeader { get; set; }
-
-        private bool IsHeadRequest { get; set; }
-
-        private long RangeStart { get; set; }
-
-        private long RangeEnd { get; set; }
-
-        private long RangeLength { get; set; }
-
-        public long TotalContentLength { get; set; }
-
-        public Action OnComplete { get; set; }
-
-        public Action OnError { get; set; }
-
-        public List<Cookie> Cookies { get; private set; }
-
-        public FileShare FileShare { get; set; }
-
-        /// <summary>
-        /// Gets the options.
-        /// </summary>
-        /// <value>The options.</value>
-        public IDictionary<string, string> Headers => _options;
-
-        public string Path { get; set; }
-
-        /// <summary>
-        /// Gets the requested ranges.
-        /// </summary>
-        /// <value>The requested ranges.</value>
-        protected List<KeyValuePair<long, long?>> RequestedRanges
-        {
-            get
-            {
-                if (_requestedRanges == null)
-                {
-                    _requestedRanges = new List<KeyValuePair<long, long?>>();
-
-                    // Example: bytes=0-,32-63
-                    var ranges = RangeHeader.Split('=')[1].Split(',');
-
-                    foreach (var range in ranges)
-                    {
-                        var vals = range.Split('-');
-
-                        long start = 0;
-                        long? end = null;
-
-                        if (!string.IsNullOrEmpty(vals[0]))
-                        {
-                            start = long.Parse(vals[0], UsCulture);
-                        }
-
-                        if (!string.IsNullOrEmpty(vals[1]))
-                        {
-                            end = long.Parse(vals[1], UsCulture);
-                        }
-
-                        _requestedRanges.Add(new KeyValuePair<long, long?>(start, end));
-                    }
-                }
-
-                return _requestedRanges;
-            }
-        }
-
-        public string ContentType { get; set; }
-
-        public IRequest RequestContext { get; set; }
-
-        public object Response { get; set; }
-
-        public int Status { get; set; }
-
-        public HttpStatusCode StatusCode
-        {
-            get => (HttpStatusCode)Status;
-            set => Status = (int)value;
-        }
-
-        /// <summary>
-        /// Sets the range values.
-        /// </summary>
-        private void SetRangeValues()
-        {
-            var requestedRange = RequestedRanges[0];
-
-            // If the requested range is "0-", we can optimize by just doing a stream copy
-            if (!requestedRange.Value.HasValue)
-            {
-                RangeEnd = TotalContentLength - 1;
-            }
-            else
-            {
-                RangeEnd = requestedRange.Value.Value;
-            }
-
-            RangeStart = requestedRange.Key;
-            RangeLength = 1 + RangeEnd - RangeStart;
-
-            // Content-Length is the length of what we're serving, not the original content
-            var lengthString = RangeLength.ToString(CultureInfo.InvariantCulture);
-            Headers[HeaderNames.ContentLength] = lengthString;
-            var rangeString = $"bytes {RangeStart}-{RangeEnd}/{TotalContentLength}";
-            Headers[HeaderNames.ContentRange] = rangeString;
-
-            _logger.LogDebug("Setting range response values for {0}. RangeRequest: {1} Content-Length: {2}, Content-Range: {3}", Path, RangeHeader, lengthString, rangeString);
-        }
-
-        public async Task WriteToAsync(HttpResponse response, CancellationToken cancellationToken)
-        {
-            try
-            {
-                // Headers only
-                if (IsHeadRequest)
-                {
-                    return;
-                }
-
-                var path = Path;
-                var offset = RangeStart;
-                var count = RangeLength;
-
-                if (string.IsNullOrWhiteSpace(RangeHeader) || RangeStart <= 0 && RangeEnd >= TotalContentLength - 1)
-                {
-                    var extension = System.IO.Path.GetExtension(path);
-
-                    if (extension == null || !_skipLogExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
-                    {
-                        _logger.LogDebug("Transmit file {0}", path);
-                    }
-
-                    offset = 0;
-                    count = 0;
-                }
-
-                await TransmitFile(response.Body, path, offset, count, FileShare, cancellationToken).ConfigureAwait(false);
-            }
-            finally
-            {
-                OnComplete?.Invoke();
-            }
-        }
-
-        public async Task TransmitFile(Stream stream, string path, long offset, long count, FileShare fileShare, CancellationToken cancellationToken)
-        {
-            var fileOptions = FileOptions.SequentialScan;
-
-            // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
-            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
-            {
-                fileOptions |= FileOptions.Asynchronous;
-            }
-
-            using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, fileShare, IODefaults.FileStreamBufferSize, fileOptions))
-            {
-                if (offset > 0)
-                {
-                    fs.Position = offset;
-                }
-
-                if (count > 0)
-                {
-                    await _streamHelper.CopyToAsync(fs, stream, count, cancellationToken).ConfigureAwait(false);
-                }
-                else
-                {
-                    await fs.CopyToAsync(stream, IODefaults.CopyToBufferSize, cancellationToken).ConfigureAwait(false);
-                }
-            }
-        }
-    }
-}

+ 22 - 211
Emby.Server.Implementations/HttpServer/HttpListenerHost.cs

@@ -7,11 +7,8 @@ using System.IO;
 using System.Linq;
 using System.Net.Sockets;
 using System.Net.WebSockets;
-using System.Reflection;
 using System.Threading;
 using System.Threading.Tasks;
-using Emby.Server.Implementations.Services;
-using Emby.Server.Implementations.SocketSharp;
 using Jellyfin.Data.Events;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Net;
@@ -20,8 +17,6 @@ using MediaBrowser.Controller.Authentication;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Model.Globalization;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Services;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Http.Extensions;
 using Microsoft.AspNetCore.WebUtilities;
@@ -29,7 +24,6 @@ using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Hosting;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Primitives;
-using ServiceStack.Text.Jsv;
 
 namespace Emby.Server.Implementations.HttpServer
 {
@@ -46,13 +40,9 @@ namespace Emby.Server.Implementations.HttpServer
         private readonly IServerConfigurationManager _config;
         private readonly INetworkManager _networkManager;
         private readonly IServerApplicationHost _appHost;
-        private readonly IJsonSerializer _jsonSerializer;
-        private readonly IXmlSerializer _xmlSerializer;
-        private readonly Func<Type, Func<string, object>> _funcParseFn;
         private readonly string _defaultRedirectPath;
         private readonly string _baseUrlPrefix;
 
-        private readonly Dictionary<Type, Type> _serviceOperationsMap = new Dictionary<Type, Type>();
         private readonly IHostEnvironment _hostEnvironment;
 
         private IWebSocketListener[] _webSocketListeners = Array.Empty<IWebSocketListener>();
@@ -64,10 +54,7 @@ namespace Emby.Server.Implementations.HttpServer
             IServerConfigurationManager config,
             IConfiguration configuration,
             INetworkManager networkManager,
-            IJsonSerializer jsonSerializer,
-            IXmlSerializer xmlSerializer,
             ILocalizationManager localizationManager,
-            ServiceController serviceController,
             IHostEnvironment hostEnvironment,
             ILoggerFactory loggerFactory)
         {
@@ -77,36 +64,21 @@ namespace Emby.Server.Implementations.HttpServer
             _defaultRedirectPath = configuration[DefaultRedirectKey];
             _baseUrlPrefix = _config.Configuration.BaseUrl;
             _networkManager = networkManager;
-            _jsonSerializer = jsonSerializer;
-            _xmlSerializer = xmlSerializer;
-            ServiceController = serviceController;
             _hostEnvironment = hostEnvironment;
             _loggerFactory = loggerFactory;
 
-            _funcParseFn = t => s => JsvReader.GetParseFn(t)(s);
-
             Instance = this;
-            ResponseFilters = Array.Empty<Action<IRequest, HttpResponse, object>>();
             GlobalResponse = localizationManager.GetLocalizedString("StartupEmbyServerIsLoading");
         }
 
         public event EventHandler<GenericEventArgs<IWebSocketConnection>> WebSocketConnected;
 
-        public Action<IRequest, HttpResponse, object>[] ResponseFilters { get; set; }
-
         public static HttpListenerHost Instance { get; protected set; }
 
         public string[] UrlPrefixes { get; private set; }
 
         public string GlobalResponse { get; set; }
 
-        public ServiceController ServiceController { get; }
-
-        public object CreateInstance(Type type)
-        {
-            return _appHost.CreateInstance(type);
-        }
-
         private static string NormalizeUrlPath(string path)
         {
             if (path.Length > 0 && path[0] == '/')
@@ -121,58 +93,6 @@ namespace Emby.Server.Implementations.HttpServer
             }
         }
 
-        /// <summary>
-        /// Applies the request filters. Returns whether or not the request has been handled
-        /// and no more processing should be done.
-        /// </summary>
-        /// <returns></returns>
-        public void ApplyRequestFilters(IRequest req, HttpResponse res, object requestDto)
-        {
-            // Exec all RequestFilter attributes with Priority < 0
-            var attributes = GetRequestFilterAttributes(requestDto.GetType());
-
-            int count = attributes.Count;
-            int i = 0;
-            for (; i < count && attributes[i].Priority < 0; i++)
-            {
-                var attribute = attributes[i];
-                attribute.RequestFilter(req, res, requestDto);
-            }
-
-            // Exec remaining RequestFilter attributes with Priority >= 0
-            for (; i < count && attributes[i].Priority >= 0; i++)
-            {
-                var attribute = attributes[i];
-                attribute.RequestFilter(req, res, requestDto);
-            }
-        }
-
-        public Type GetServiceTypeByRequest(Type requestType)
-        {
-            _serviceOperationsMap.TryGetValue(requestType, out var serviceType);
-            return serviceType;
-        }
-
-        public void AddServiceInfo(Type serviceType, Type requestType)
-        {
-            _serviceOperationsMap[requestType] = serviceType;
-        }
-
-        private List<IHasRequestFilter> GetRequestFilterAttributes(Type requestDtoType)
-        {
-            var attributes = requestDtoType.GetCustomAttributes(true).OfType<IHasRequestFilter>().ToList();
-
-            var serviceType = GetServiceTypeByRequest(requestDtoType);
-            if (serviceType != null)
-            {
-                attributes.AddRange(serviceType.GetCustomAttributes(true).OfType<IHasRequestFilter>());
-            }
-
-            attributes.Sort((x, y) => x.Priority - y.Priority);
-
-            return attributes;
-        }
-
         private static Exception GetActualException(Exception ex)
         {
             if (ex is AggregateException agg)
@@ -210,7 +130,7 @@ namespace Emby.Server.Implementations.HttpServer
             }
         }
 
-        private async Task ErrorHandler(Exception ex, IRequest httpReq, int statusCode, string urlToLog, bool ignoreStackTrace)
+        private async Task ErrorHandler(Exception ex, HttpContext httpContext, int statusCode, string urlToLog, bool ignoreStackTrace)
         {
             if (ignoreStackTrace)
             {
@@ -221,7 +141,7 @@ namespace Emby.Server.Implementations.HttpServer
                 _logger.LogError(ex, "Error processing request. URL: {Url}", urlToLog);
             }
 
-            var httpRes = httpReq.Response;
+            var httpRes = httpContext.Response;
 
             if (httpRes.HasStarted)
             {
@@ -395,24 +315,22 @@ namespace Emby.Server.Implementations.HttpServer
                 return WebSocketRequestHandler(context);
             }
 
-            var request = context.Request;
-            var response = context.Response;
-            var localPath = context.Request.Path.ToString();
-
-            var req = new WebSocketSharpRequest(request, response, request.Path);
-            return RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted);
+            return RequestHandler(context, context.RequestAborted);
         }
 
         /// <summary>
         /// Overridable method that can be used to implement a custom handler.
         /// </summary>
-        private async Task RequestHandler(IHttpRequest httpReq, string urlString, string host, string localPath, CancellationToken cancellationToken)
+        private async Task RequestHandler(HttpContext httpContext, CancellationToken cancellationToken)
         {
             var stopWatch = new Stopwatch();
             stopWatch.Start();
-            var httpRes = httpReq.Response;
+            var httpRes = httpContext.Response;
+            var host = httpContext.Request.Host.ToString();
+            var localPath = httpContext.Request.Path.ToString();
+            var urlString = httpContext.Request.GetDisplayUrl();
             string urlToLog = GetUrlToLog(urlString);
-            string remoteIp = httpReq.RemoteIp;
+            string remoteIp = httpContext.Request.RemoteIp();
 
             try
             {
@@ -432,7 +350,7 @@ namespace Emby.Server.Implementations.HttpServer
                     return;
                 }
 
-                if (!ValidateRequest(remoteIp, httpReq.IsLocal))
+                if (!ValidateRequest(remoteIp, httpContext.Request.IsLocal()))
                 {
                     httpRes.StatusCode = 403;
                     httpRes.ContentType = "text/plain";
@@ -440,16 +358,16 @@ namespace Emby.Server.Implementations.HttpServer
                     return;
                 }
 
-                if (!ValidateSsl(httpReq.RemoteIp, urlString))
+                if (!ValidateSsl(httpContext.Request.RemoteIp(), urlString))
                 {
-                    RedirectToSecureUrl(httpReq, httpRes, urlString);
+                    RedirectToSecureUrl(httpRes, urlString);
                     return;
                 }
 
-                if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase))
+                if (string.Equals(httpContext.Request.Method, "OPTIONS", StringComparison.OrdinalIgnoreCase))
                 {
                     httpRes.StatusCode = 200;
-                    foreach (var (key, value) in GetDefaultCorsHeaders(httpReq))
+                    foreach (var (key, value) in GetDefaultCorsHeaders(httpContext))
                     {
                         httpRes.Headers.Add(key, value);
                     }
@@ -483,15 +401,7 @@ namespace Emby.Server.Implementations.HttpServer
                     }
                 }
 
-                var handler = GetServiceHandler(httpReq);
-                if (handler != null)
-                {
-                    await handler.ProcessRequestAsync(this, httpReq, httpRes, cancellationToken).ConfigureAwait(false);
-                }
-                else
-                {
-                    throw new FileNotFoundException();
-                }
+                throw new FileNotFoundException();
             }
             catch (Exception requestEx)
             {
@@ -500,7 +410,7 @@ namespace Emby.Server.Implementations.HttpServer
                     var requestInnerEx = GetActualException(requestEx);
                     var statusCode = GetStatusCode(requestInnerEx);
 
-                    foreach (var (key, value) in GetDefaultCorsHeaders(httpReq))
+                    foreach (var (key, value) in GetDefaultCorsHeaders(httpContext))
                     {
                         if (!httpRes.Headers.ContainsKey(key))
                         {
@@ -525,7 +435,7 @@ namespace Emby.Server.Implementations.HttpServer
                         throw;
                     }
 
-                    await ErrorHandler(requestInnerEx, httpReq, statusCode, urlToLog, ignoreStackTrace).ConfigureAwait(false);
+                    await ErrorHandler(requestInnerEx, httpContext, statusCode, urlToLog, ignoreStackTrace).ConfigureAwait(false);
                 }
                 catch (Exception handlerException)
                 {
@@ -596,12 +506,12 @@ namespace Emby.Server.Implementations.HttpServer
         /// </summary>
         /// <param name="req"></param>
         /// <returns></returns>
-        public IDictionary<string, string> GetDefaultCorsHeaders(IRequest req)
+        public IDictionary<string, string> GetDefaultCorsHeaders(HttpContext httpContext)
         {
-            var origin = req.Headers["Origin"];
+            var origin = httpContext.Request.Headers["Origin"];
             if (origin == StringValues.Empty)
             {
-                origin = req.Headers["Host"];
+                origin = httpContext.Request.Headers["Host"];
                 if (origin == StringValues.Empty)
                 {
                     origin = "*";
@@ -616,23 +526,7 @@ namespace Emby.Server.Implementations.HttpServer
             return headers;
         }
 
-        // Entry point for HttpListener
-        public ServiceHandler GetServiceHandler(IHttpRequest httpReq)
-        {
-            var pathInfo = httpReq.PathInfo;
-
-            pathInfo = ServiceHandler.GetSanitizedPathInfo(pathInfo, out string contentType);
-            var restPath = ServiceController.GetRestPathForRequest(httpReq.HttpMethod, pathInfo);
-            if (restPath != null)
-            {
-                return new ServiceHandler(restPath, contentType);
-            }
-
-            _logger.LogError("Could not find handler for {PathInfo}", pathInfo);
-            return null;
-        }
-
-        private void RedirectToSecureUrl(IHttpRequest httpReq, HttpResponse httpRes, string url)
+        private void RedirectToSecureUrl(HttpResponse httpRes, string url)
         {
             if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri))
             {
@@ -650,95 +544,12 @@ namespace Emby.Server.Implementations.HttpServer
         /// <summary>
         /// Adds the rest handlers.
         /// </summary>
-        /// <param name="serviceTypes">The service types to register with the <see cref="ServiceController"/>.</param>
         /// <param name="listeners">The web socket listeners.</param>
         /// <param name="urlPrefixes">The URL prefixes. See <see cref="UrlPrefixes"/>.</param>
-        public void Init(IEnumerable<Type> serviceTypes, IEnumerable<IWebSocketListener> listeners, IEnumerable<string> urlPrefixes)
+        public void Init(IEnumerable<IWebSocketListener> listeners, IEnumerable<string> urlPrefixes)
         {
             _webSocketListeners = listeners.ToArray();
             UrlPrefixes = urlPrefixes.ToArray();
-
-            ServiceController.Init(this, serviceTypes);
-
-            ResponseFilters = new Action<IRequest, HttpResponse, object>[]
-            {
-                new ResponseFilter(this, _logger).FilterResponse
-            };
-        }
-
-        public RouteAttribute[] GetRouteAttributes(Type requestType)
-        {
-            var routes = requestType.GetTypeInfo().GetCustomAttributes<RouteAttribute>(true).ToList();
-            var clone = routes.ToList();
-
-            foreach (var route in clone)
-            {
-                routes.Add(new RouteAttribute(NormalizeCustomRoutePath(route.Path), route.Verbs)
-                {
-                    Notes = route.Notes,
-                    Priority = route.Priority,
-                    Summary = route.Summary
-                });
-
-                routes.Add(new RouteAttribute(NormalizeEmbyRoutePath(route.Path), route.Verbs)
-                {
-                    Notes = route.Notes,
-                    Priority = route.Priority,
-                    Summary = route.Summary
-                });
-
-                routes.Add(new RouteAttribute(NormalizeMediaBrowserRoutePath(route.Path), route.Verbs)
-                {
-                    Notes = route.Notes,
-                    Priority = route.Priority,
-                    Summary = route.Summary
-                });
-            }
-
-            return routes.ToArray();
-        }
-
-        public Func<string, object> GetParseFn(Type propertyType)
-        {
-            return _funcParseFn(propertyType);
-        }
-
-        public void SerializeToJson(object o, Stream stream)
-        {
-            _jsonSerializer.SerializeToStream(o, stream);
-        }
-
-        public void SerializeToXml(object o, Stream stream)
-        {
-            _xmlSerializer.SerializeToStream(o, stream);
-        }
-
-        public Task<object> DeserializeXml(Type type, Stream stream)
-        {
-            return Task.FromResult(_xmlSerializer.DeserializeFromStream(type, stream));
-        }
-
-        public Task<object> DeserializeJson(Type type, Stream stream)
-        {
-            return _jsonSerializer.DeserializeFromStreamAsync(stream, type);
-        }
-
-        private string NormalizeEmbyRoutePath(string path)
-        {
-            _logger.LogDebug("Normalizing /emby route");
-            return _baseUrlPrefix + "/emby" + NormalizeUrlPath(path);
-        }
-
-        private string NormalizeMediaBrowserRoutePath(string path)
-        {
-            _logger.LogDebug("Normalizing /mediabrowser route");
-            return _baseUrlPrefix + "/mediabrowser" + NormalizeUrlPath(path);
-        }
-
-        private string NormalizeCustomRoutePath(string path)
-        {
-            _logger.LogDebug("Normalizing custom route {0}", path);
-            return _baseUrlPrefix + NormalizeUrlPath(path);
         }
 
         /// <summary>

+ 0 - 721
Emby.Server.Implementations/HttpServer/HttpResultFactory.cs

@@ -1,721 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.IO.Compression;
-using System.Net;
-using System.Runtime.Serialization;
-using System.Text;
-using System.Threading.Tasks;
-using System.Xml;
-using Emby.Server.Implementations.Services;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Services;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Primitives;
-using Microsoft.Net.Http.Headers;
-using IRequest = MediaBrowser.Model.Services.IRequest;
-using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
-
-namespace Emby.Server.Implementations.HttpServer
-{
-    /// <summary>
-    /// Class HttpResultFactory.
-    /// </summary>
-    public class HttpResultFactory : IHttpResultFactory
-    {
-        // Last-Modified and If-Modified-Since must follow strict date format,
-        // see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Modified-Since
-        private const string HttpDateFormat = "ddd, dd MMM yyyy HH:mm:ss \"GMT\"";
-        // We specifically use en-US culture because both day of week and month names require it
-        private static readonly CultureInfo _enUSculture = new CultureInfo("en-US", false);
-
-        /// <summary>
-        /// The logger.
-        /// </summary>
-        private readonly ILogger<HttpResultFactory> _logger;
-        private readonly IFileSystem _fileSystem;
-        private readonly IJsonSerializer _jsonSerializer;
-        private readonly IStreamHelper _streamHelper;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="HttpResultFactory" /> class.
-        /// </summary>
-        public HttpResultFactory(ILoggerFactory loggerfactory, IFileSystem fileSystem, IJsonSerializer jsonSerializer, IStreamHelper streamHelper)
-        {
-            _fileSystem = fileSystem;
-            _jsonSerializer = jsonSerializer;
-            _streamHelper = streamHelper;
-            _logger = loggerfactory.CreateLogger<HttpResultFactory>();
-        }
-
-        /// <summary>
-        /// Gets the result.
-        /// </summary>
-        /// <param name="requestContext">The request context.</param>
-        /// <param name="content">The content.</param>
-        /// <param name="contentType">Type of the content.</param>
-        /// <param name="responseHeaders">The response headers.</param>
-        /// <returns>System.Object.</returns>
-        public object GetResult(IRequest requestContext, byte[] content, string contentType, IDictionary<string, string> responseHeaders = null)
-        {
-            return GetHttpResult(requestContext, content, contentType, true, responseHeaders);
-        }
-
-        public object GetResult(string content, string contentType, IDictionary<string, string> responseHeaders = null)
-        {
-            return GetHttpResult(null, content, contentType, true, responseHeaders);
-        }
-
-        public object GetResult(IRequest requestContext, Stream content, string contentType, IDictionary<string, string> responseHeaders = null)
-        {
-            return GetHttpResult(requestContext, content, contentType, true, responseHeaders);
-        }
-
-        public object GetResult(IRequest requestContext, string content, string contentType, IDictionary<string, string> responseHeaders = null)
-        {
-            return GetHttpResult(requestContext, content, contentType, true, responseHeaders);
-        }
-
-        public object GetRedirectResult(string url)
-        {
-            var responseHeaders = new Dictionary<string, string>();
-            responseHeaders[HeaderNames.Location] = url;
-
-            var result = new HttpResult(Array.Empty<byte>(), "text/plain", HttpStatusCode.Redirect);
-
-            AddResponseHeaders(result, responseHeaders);
-
-            return result;
-        }
-
-        /// <summary>
-        /// Gets the HTTP result.
-        /// </summary>
-        private IHasHeaders GetHttpResult(IRequest requestContext, Stream content, string contentType, bool addCachePrevention, IDictionary<string, string> responseHeaders = null)
-        {
-            var result = new StreamWriter(content, contentType);
-
-            if (responseHeaders == null)
-            {
-                responseHeaders = new Dictionary<string, string>();
-            }
-
-            if (addCachePrevention && !responseHeaders.TryGetValue(HeaderNames.Expires, out _))
-            {
-                responseHeaders[HeaderNames.Expires] = "0";
-            }
-
-            AddResponseHeaders(result, responseHeaders);
-
-            return result;
-        }
-
-        /// <summary>
-        /// Gets the HTTP result.
-        /// </summary>
-        private IHasHeaders GetHttpResult(IRequest requestContext, byte[] content, string contentType, bool addCachePrevention, IDictionary<string, string> responseHeaders = null)
-        {
-            string compressionType = null;
-            bool isHeadRequest = false;
-
-            if (requestContext != null)
-            {
-                compressionType = GetCompressionType(requestContext, content, contentType);
-                isHeadRequest = string.Equals(requestContext.Verb, "head", StringComparison.OrdinalIgnoreCase);
-            }
-
-            IHasHeaders result;
-            if (string.IsNullOrEmpty(compressionType))
-            {
-                var contentLength = content.Length;
-
-                if (isHeadRequest)
-                {
-                    content = Array.Empty<byte>();
-                }
-
-                result = new StreamWriter(content, contentType, contentLength);
-            }
-            else
-            {
-                result = GetCompressedResult(content, compressionType, responseHeaders, isHeadRequest, contentType);
-            }
-
-            if (responseHeaders == null)
-            {
-                responseHeaders = new Dictionary<string, string>();
-            }
-
-            if (addCachePrevention && !responseHeaders.TryGetValue(HeaderNames.Expires, out string _))
-            {
-                responseHeaders[HeaderNames.Expires] = "0";
-            }
-
-            AddResponseHeaders(result, responseHeaders);
-
-            return result;
-        }
-
-        /// <summary>
-        /// Gets the HTTP result.
-        /// </summary>
-        private IHasHeaders GetHttpResult(IRequest requestContext, string content, string contentType, bool addCachePrevention, IDictionary<string, string> responseHeaders = null)
-        {
-            IHasHeaders result;
-
-            var bytes = Encoding.UTF8.GetBytes(content);
-
-            var compressionType = requestContext == null ? null : GetCompressionType(requestContext, bytes, contentType);
-
-            var isHeadRequest = requestContext == null ? false : string.Equals(requestContext.Verb, "head", StringComparison.OrdinalIgnoreCase);
-
-            if (string.IsNullOrEmpty(compressionType))
-            {
-                var contentLength = bytes.Length;
-
-                if (isHeadRequest)
-                {
-                    bytes = Array.Empty<byte>();
-                }
-
-                result = new StreamWriter(bytes, contentType, contentLength);
-            }
-            else
-            {
-                result = GetCompressedResult(bytes, compressionType, responseHeaders, isHeadRequest, contentType);
-            }
-
-            if (responseHeaders == null)
-            {
-                responseHeaders = new Dictionary<string, string>();
-            }
-
-            if (addCachePrevention && !responseHeaders.TryGetValue(HeaderNames.Expires, out string _))
-            {
-                responseHeaders[HeaderNames.Expires] = "0";
-            }
-
-            AddResponseHeaders(result, responseHeaders);
-
-            return result;
-        }
-
-        /// <summary>
-        /// Gets the optimized result.
-        /// </summary>
-        /// <typeparam name="T"></typeparam>
-        public object GetResult<T>(IRequest requestContext, T result, IDictionary<string, string> responseHeaders = null)
-            where T : class
-        {
-            if (result == null)
-            {
-                throw new ArgumentNullException(nameof(result));
-            }
-
-            if (responseHeaders == null)
-            {
-                responseHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-            }
-
-            responseHeaders[HeaderNames.Expires] = "0";
-
-            return ToOptimizedResultInternal(requestContext, result, responseHeaders);
-        }
-
-        private string GetCompressionType(IRequest request, byte[] content, string responseContentType)
-        {
-            if (responseContentType == null)
-            {
-                return null;
-            }
-
-            // Per apple docs, hls manifests must be compressed
-            if (!responseContentType.StartsWith("text/", StringComparison.OrdinalIgnoreCase) &&
-                responseContentType.IndexOf("json", StringComparison.OrdinalIgnoreCase) == -1 &&
-                responseContentType.IndexOf("javascript", StringComparison.OrdinalIgnoreCase) == -1 &&
-                responseContentType.IndexOf("xml", StringComparison.OrdinalIgnoreCase) == -1 &&
-                responseContentType.IndexOf("application/x-mpegURL", StringComparison.OrdinalIgnoreCase) == -1)
-            {
-                return null;
-            }
-
-            if (content.Length < 1024)
-            {
-                return null;
-            }
-
-            return GetCompressionType(request);
-        }
-
-        private static string GetCompressionType(IRequest request)
-        {
-            var acceptEncoding = request.Headers[HeaderNames.AcceptEncoding].ToString();
-
-            if (!string.IsNullOrEmpty(acceptEncoding))
-            {
-                // if (_brotliCompressor != null && acceptEncoding.IndexOf("br", StringComparison.OrdinalIgnoreCase) != -1)
-                //    return "br";
-
-                if (acceptEncoding.Contains("deflate", StringComparison.OrdinalIgnoreCase))
-                {
-                    return "deflate";
-                }
-
-                if (acceptEncoding.Contains("gzip", StringComparison.OrdinalIgnoreCase))
-                {
-                    return "gzip";
-                }
-            }
-
-            return null;
-        }
-
-        /// <summary>
-        /// Returns the optimized result for the IRequestContext.
-        /// Does not use or store results in any cache.
-        /// </summary>
-        /// <param name="request"></param>
-        /// <param name="dto"></param>
-        /// <returns></returns>
-        public object ToOptimizedResult<T>(IRequest request, T dto)
-        {
-            return ToOptimizedResultInternal(request, dto);
-        }
-
-        private object ToOptimizedResultInternal<T>(IRequest request, T dto, IDictionary<string, string> responseHeaders = null)
-        {
-            // TODO: @bond use Span and .Equals
-            var contentType = request.ResponseContentType?.Split(';')[0].Trim().ToLowerInvariant();
-
-            switch (contentType)
-            {
-                case "application/xml":
-                case "text/xml":
-                case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml
-                    return GetHttpResult(request, SerializeToXmlString(dto), contentType, false, responseHeaders);
-
-                case "application/json":
-                case "text/json":
-                    return GetHttpResult(request, _jsonSerializer.SerializeToString(dto), contentType, false, responseHeaders);
-                default:
-                    break;
-            }
-
-            var isHeadRequest = string.Equals(request.Verb, "head", StringComparison.OrdinalIgnoreCase);
-
-            var ms = new MemoryStream();
-            var writerFn = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType);
-
-            writerFn(dto, ms);
-
-            ms.Position = 0;
-
-            if (isHeadRequest)
-            {
-                using (ms)
-                {
-                    return GetHttpResult(request, Array.Empty<byte>(), contentType, true, responseHeaders);
-                }
-            }
-
-            return GetHttpResult(request, ms, contentType, true, responseHeaders);
-        }
-
-        private IHasHeaders GetCompressedResult(
-            byte[] content,
-            string requestedCompressionType,
-            IDictionary<string, string> responseHeaders,
-            bool isHeadRequest,
-            string contentType)
-        {
-            if (responseHeaders == null)
-            {
-                responseHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-            }
-
-            content = Compress(content, requestedCompressionType);
-            responseHeaders[HeaderNames.ContentEncoding] = requestedCompressionType;
-
-            responseHeaders[HeaderNames.Vary] = HeaderNames.AcceptEncoding;
-
-            var contentLength = content.Length;
-
-            if (isHeadRequest)
-            {
-                var result = new StreamWriter(Array.Empty<byte>(), contentType, contentLength);
-                AddResponseHeaders(result, responseHeaders);
-                return result;
-            }
-            else
-            {
-                var result = new StreamWriter(content, contentType, contentLength);
-                AddResponseHeaders(result, responseHeaders);
-                return result;
-            }
-        }
-
-        private byte[] Compress(byte[] bytes, string compressionType)
-        {
-            if (string.Equals(compressionType, "deflate", StringComparison.OrdinalIgnoreCase))
-            {
-                return Deflate(bytes);
-            }
-
-            if (string.Equals(compressionType, "gzip", StringComparison.OrdinalIgnoreCase))
-            {
-                return GZip(bytes);
-            }
-
-            throw new NotSupportedException(compressionType);
-        }
-
-        private static byte[] Deflate(byte[] bytes)
-        {
-            // In .NET FX incompat-ville, you can't access compressed bytes without closing DeflateStream
-            // Which means we must use MemoryStream since you have to use ToArray() on a closed Stream
-            using (var ms = new MemoryStream())
-            using (var zipStream = new DeflateStream(ms, CompressionMode.Compress))
-            {
-                zipStream.Write(bytes, 0, bytes.Length);
-                zipStream.Dispose();
-
-                return ms.ToArray();
-            }
-        }
-
-        private static byte[] GZip(byte[] buffer)
-        {
-            using (var ms = new MemoryStream())
-            using (var zipStream = new GZipStream(ms, CompressionMode.Compress))
-            {
-                zipStream.Write(buffer, 0, buffer.Length);
-                zipStream.Dispose();
-
-                return ms.ToArray();
-            }
-        }
-
-        private static string SerializeToXmlString(object from)
-        {
-            using (var ms = new MemoryStream())
-            {
-                var xwSettings = new XmlWriterSettings();
-                xwSettings.Encoding = new UTF8Encoding(false);
-                xwSettings.OmitXmlDeclaration = false;
-
-                using (var xw = XmlWriter.Create(ms, xwSettings))
-                {
-                    var serializer = new DataContractSerializer(from.GetType());
-                    serializer.WriteObject(xw, from);
-                    xw.Flush();
-                    ms.Seek(0, SeekOrigin.Begin);
-                    using (var reader = new StreamReader(ms))
-                    {
-                        return reader.ReadToEnd();
-                    }
-                }
-            }
-        }
-
-        /// <summary>
-        /// Pres the process optimized result.
-        /// </summary>
-        private object GetCachedResult(IRequest requestContext, IDictionary<string, string> responseHeaders, StaticResultOptions options)
-        {
-            bool noCache = requestContext.Headers[HeaderNames.CacheControl].ToString().IndexOf("no-cache", StringComparison.OrdinalIgnoreCase) != -1;
-            AddCachingHeaders(responseHeaders, options.CacheDuration, noCache, options.DateLastModified);
-
-            if (!noCache)
-            {
-                if (!DateTime.TryParseExact(requestContext.Headers[HeaderNames.IfModifiedSince], HttpDateFormat, _enUSculture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out var ifModifiedSinceHeader))
-                {
-                    _logger.LogDebug("Failed to parse If-Modified-Since header date: {0}", requestContext.Headers[HeaderNames.IfModifiedSince]);
-                    return null;
-                }
-
-                if (IsNotModified(ifModifiedSinceHeader, options.CacheDuration, options.DateLastModified))
-                {
-                    AddAgeHeader(responseHeaders, options.DateLastModified);
-
-                    var result = new HttpResult(Array.Empty<byte>(), options.ContentType ?? "text/html", HttpStatusCode.NotModified);
-
-                    AddResponseHeaders(result, responseHeaders);
-
-                    return result;
-                }
-            }
-
-            return null;
-        }
-
-        public Task<object> GetStaticFileResult(IRequest requestContext,
-            string path,
-            FileShare fileShare = FileShare.Read)
-        {
-            if (string.IsNullOrEmpty(path))
-            {
-                throw new ArgumentNullException(nameof(path));
-            }
-
-            return GetStaticFileResult(requestContext, new StaticFileResultOptions
-            {
-                Path = path,
-                FileShare = fileShare
-            });
-        }
-
-        public Task<object> GetStaticFileResult(IRequest requestContext, StaticFileResultOptions options)
-        {
-            var path = options.Path;
-            var fileShare = options.FileShare;
-
-            if (string.IsNullOrEmpty(path))
-            {
-                throw new ArgumentException("Path can't be empty.", nameof(options));
-            }
-
-            if (fileShare != FileShare.Read && fileShare != FileShare.ReadWrite)
-            {
-                throw new ArgumentException("FileShare must be either Read or ReadWrite");
-            }
-
-            if (string.IsNullOrEmpty(options.ContentType))
-            {
-                options.ContentType = MimeTypes.GetMimeType(path);
-            }
-
-            if (!options.DateLastModified.HasValue)
-            {
-                options.DateLastModified = _fileSystem.GetLastWriteTimeUtc(path);
-            }
-
-            options.ContentFactory = () => Task.FromResult(GetFileStream(path, fileShare));
-
-            options.ResponseHeaders = options.ResponseHeaders ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-
-            return GetStaticResult(requestContext, options);
-        }
-
-        /// <summary>
-        /// Gets the file stream.
-        /// </summary>
-        /// <param name="path">The path.</param>
-        /// <param name="fileShare">The file share.</param>
-        /// <returns>Stream.</returns>
-        private Stream GetFileStream(string path, FileShare fileShare)
-        {
-            return new FileStream(path, FileMode.Open, FileAccess.Read, fileShare);
-        }
-
-        public Task<object> GetStaticResult(IRequest requestContext,
-            Guid cacheKey,
-            DateTime? lastDateModified,
-            TimeSpan? cacheDuration,
-            string contentType,
-            Func<Task<Stream>> factoryFn,
-            IDictionary<string, string> responseHeaders = null,
-            bool isHeadRequest = false)
-        {
-            return GetStaticResult(requestContext, new StaticResultOptions
-            {
-                CacheDuration = cacheDuration,
-                ContentFactory = factoryFn,
-                ContentType = contentType,
-                DateLastModified = lastDateModified,
-                IsHeadRequest = isHeadRequest,
-                ResponseHeaders = responseHeaders
-            });
-        }
-
-        public async Task<object> GetStaticResult(IRequest requestContext, StaticResultOptions options)
-        {
-            options.ResponseHeaders = options.ResponseHeaders ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-
-            var contentType = options.ContentType;
-            if (!StringValues.IsNullOrEmpty(requestContext.Headers[HeaderNames.IfModifiedSince]))
-            {
-                // See if the result is already cached in the browser
-                var result = GetCachedResult(requestContext, options.ResponseHeaders, options);
-
-                if (result != null)
-                {
-                    return result;
-                }
-            }
-
-            // TODO: We don't really need the option value
-            var isHeadRequest = options.IsHeadRequest || string.Equals(requestContext.Verb, "HEAD", StringComparison.OrdinalIgnoreCase);
-            var factoryFn = options.ContentFactory;
-            var responseHeaders = options.ResponseHeaders;
-            AddCachingHeaders(responseHeaders, options.CacheDuration, false, options.DateLastModified);
-            AddAgeHeader(responseHeaders, options.DateLastModified);
-
-            var rangeHeader = requestContext.Headers[HeaderNames.Range];
-
-            if (!isHeadRequest && !string.IsNullOrEmpty(options.Path))
-            {
-                var hasHeaders = new FileWriter(options.Path, contentType, rangeHeader, _logger, _fileSystem, _streamHelper)
-                {
-                    OnComplete = options.OnComplete,
-                    OnError = options.OnError,
-                    FileShare = options.FileShare
-                };
-
-                AddResponseHeaders(hasHeaders, options.ResponseHeaders);
-                return hasHeaders;
-            }
-
-            var stream = await factoryFn().ConfigureAwait(false);
-
-            var totalContentLength = options.ContentLength;
-            if (!totalContentLength.HasValue)
-            {
-                try
-                {
-                    totalContentLength = stream.Length;
-                }
-                catch (NotSupportedException)
-                {
-                }
-            }
-
-            if (!string.IsNullOrWhiteSpace(rangeHeader) && totalContentLength.HasValue)
-            {
-                var hasHeaders = new RangeRequestWriter(rangeHeader, totalContentLength.Value, stream, contentType, isHeadRequest)
-                {
-                    OnComplete = options.OnComplete
-                };
-
-                AddResponseHeaders(hasHeaders, options.ResponseHeaders);
-                return hasHeaders;
-            }
-            else
-            {
-                if (totalContentLength.HasValue)
-                {
-                    responseHeaders["Content-Length"] = totalContentLength.Value.ToString(CultureInfo.InvariantCulture);
-                }
-
-                if (isHeadRequest)
-                {
-                    using (stream)
-                    {
-                        return GetHttpResult(requestContext, Array.Empty<byte>(), contentType, true, responseHeaders);
-                    }
-                }
-
-                var hasHeaders = new StreamWriter(stream, contentType)
-                {
-                    OnComplete = options.OnComplete,
-                    OnError = options.OnError
-                };
-
-                AddResponseHeaders(hasHeaders, options.ResponseHeaders);
-                return hasHeaders;
-            }
-        }
-
-        /// <summary>
-        /// Adds the caching responseHeaders.
-        /// </summary>
-        private void AddCachingHeaders(
-            IDictionary<string, string> responseHeaders,
-            TimeSpan? cacheDuration,
-            bool noCache,
-            DateTime? lastModifiedDate)
-        {
-            if (noCache)
-            {
-                responseHeaders[HeaderNames.CacheControl] = "no-cache, no-store, must-revalidate";
-                responseHeaders[HeaderNames.Pragma] = "no-cache, no-store, must-revalidate";
-                return;
-            }
-
-            if (cacheDuration.HasValue)
-            {
-                responseHeaders[HeaderNames.CacheControl] = "public, max-age=" + cacheDuration.Value.TotalSeconds;
-            }
-            else
-            {
-                responseHeaders[HeaderNames.CacheControl] = "public";
-            }
-
-            if (lastModifiedDate.HasValue)
-            {
-                responseHeaders[HeaderNames.LastModified] = lastModifiedDate.Value.ToUniversalTime().ToString(HttpDateFormat, _enUSculture);
-            }
-        }
-
-        /// <summary>
-        /// Adds the age header.
-        /// </summary>
-        /// <param name="responseHeaders">The responseHeaders.</param>
-        /// <param name="lastDateModified">The last date modified.</param>
-        private static void AddAgeHeader(IDictionary<string, string> responseHeaders, DateTime? lastDateModified)
-        {
-            if (lastDateModified.HasValue)
-            {
-                responseHeaders[HeaderNames.Age] = Convert.ToInt64((DateTime.UtcNow - lastDateModified.Value).TotalSeconds).ToString(CultureInfo.InvariantCulture);
-            }
-        }
-
-        /// <summary>
-        /// Determines whether [is not modified] [the specified if modified since].
-        /// </summary>
-        /// <param name="ifModifiedSince">If modified since.</param>
-        /// <param name="cacheDuration">Duration of the cache.</param>
-        /// <param name="dateModified">The date modified.</param>
-        /// <returns><c>true</c> if [is not modified] [the specified if modified since]; otherwise, <c>false</c>.</returns>
-        private bool IsNotModified(DateTime ifModifiedSince, TimeSpan? cacheDuration, DateTime? dateModified)
-        {
-            if (dateModified.HasValue)
-            {
-                var lastModified = NormalizeDateForComparison(dateModified.Value);
-                ifModifiedSince = NormalizeDateForComparison(ifModifiedSince);
-
-                return lastModified <= ifModifiedSince;
-            }
-
-            if (cacheDuration.HasValue)
-            {
-                var cacheExpirationDate = ifModifiedSince.Add(cacheDuration.Value);
-
-                if (DateTime.UtcNow < cacheExpirationDate)
-                {
-                    return true;
-                }
-            }
-
-            return false;
-        }
-
-
-        /// <summary>
-        /// When the browser sends the IfModifiedDate, it's precision is limited to seconds, so this will account for that.
-        /// </summary>
-        /// <param name="date">The date.</param>
-        /// <returns>DateTime.</returns>
-        private static DateTime NormalizeDateForComparison(DateTime date)
-        {
-            return new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, date.Kind);
-        }
-
-        /// <summary>
-        /// Adds the response headers.
-        /// </summary>
-        /// <param name="hasHeaders">The has options.</param>
-        /// <param name="responseHeaders">The response headers.</param>
-        private static void AddResponseHeaders(IHasHeaders hasHeaders, IEnumerable<KeyValuePair<string, string>> responseHeaders)
-        {
-            foreach (var item in responseHeaders)
-            {
-                hasHeaders.Headers[item.Key] = item.Value;
-            }
-        }
-    }
-}

+ 0 - 212
Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs

@@ -1,212 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Buffers;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Net;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Services;
-using Microsoft.Net.Http.Headers;
-
-namespace Emby.Server.Implementations.HttpServer
-{
-    public class RangeRequestWriter : IAsyncStreamWriter, IHttpResult
-    {
-        private const int BufferSize = 81920;
-
-        private readonly Dictionary<string, string> _options = new Dictionary<string, string>();
-
-        private List<KeyValuePair<long, long?>> _requestedRanges;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="RangeRequestWriter" /> class.
-        /// </summary>
-        /// <param name="rangeHeader">The range header.</param>
-        /// <param name="contentLength">The content length.</param>
-        /// <param name="source">The source.</param>
-        /// <param name="contentType">Type of the content.</param>
-        /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
-        public RangeRequestWriter(string rangeHeader, long contentLength, Stream source, string contentType, bool isHeadRequest)
-        {
-            if (string.IsNullOrEmpty(contentType))
-            {
-                throw new ArgumentNullException(nameof(contentType));
-            }
-
-            RangeHeader = rangeHeader;
-            SourceStream = source;
-            IsHeadRequest = isHeadRequest;
-
-            ContentType = contentType;
-            Headers[HeaderNames.ContentType] = contentType;
-            Headers[HeaderNames.AcceptRanges] = "bytes";
-            StatusCode = HttpStatusCode.PartialContent;
-
-            SetRangeValues(contentLength);
-        }
-
-        /// <summary>
-        /// Gets or sets the source stream.
-        /// </summary>
-        /// <value>The source stream.</value>
-        private Stream SourceStream { get; set; }
-        private string RangeHeader { get; set; }
-        private bool IsHeadRequest { get; set; }
-
-        private long RangeStart { get; set; }
-        private long RangeEnd { get; set; }
-        private long RangeLength { get; set; }
-        private long TotalContentLength { get; set; }
-
-        public Action OnComplete { get; set; }
-
-        /// <summary>
-        /// Additional HTTP Headers
-        /// </summary>
-        /// <value>The headers.</value>
-        public IDictionary<string, string> Headers => _options;
-
-        /// <summary>
-        /// Gets the requested ranges.
-        /// </summary>
-        /// <value>The requested ranges.</value>
-        protected List<KeyValuePair<long, long?>> RequestedRanges
-        {
-            get
-            {
-                if (_requestedRanges == null)
-                {
-                    _requestedRanges = new List<KeyValuePair<long, long?>>();
-
-                    // Example: bytes=0-,32-63
-                    var ranges = RangeHeader.Split('=')[1].Split(',');
-
-                    foreach (var range in ranges)
-                    {
-                        var vals = range.Split('-');
-
-                        long start = 0;
-                        long? end = null;
-
-                        if (!string.IsNullOrEmpty(vals[0]))
-                        {
-                            start = long.Parse(vals[0], CultureInfo.InvariantCulture);
-                        }
-
-                        if (!string.IsNullOrEmpty(vals[1]))
-                        {
-                            end = long.Parse(vals[1], CultureInfo.InvariantCulture);
-                        }
-
-                        _requestedRanges.Add(new KeyValuePair<long, long?>(start, end));
-                    }
-                }
-
-                return _requestedRanges;
-            }
-        }
-
-        public string ContentType { get; set; }
-
-        public IRequest RequestContext { get; set; }
-
-        public object Response { get; set; }
-
-        public int Status { get; set; }
-
-        public HttpStatusCode StatusCode
-        {
-            get => (HttpStatusCode)Status;
-            set => Status = (int)value;
-        }
-
-        /// <summary>
-        /// Sets the range values.
-        /// </summary>
-        private void SetRangeValues(long contentLength)
-        {
-            var requestedRange = RequestedRanges[0];
-
-            TotalContentLength = contentLength;
-
-            // If the requested range is "0-", we can optimize by just doing a stream copy
-            if (!requestedRange.Value.HasValue)
-            {
-                RangeEnd = TotalContentLength - 1;
-            }
-            else
-            {
-                RangeEnd = requestedRange.Value.Value;
-            }
-
-            RangeStart = requestedRange.Key;
-            RangeLength = 1 + RangeEnd - RangeStart;
-
-            Headers[HeaderNames.ContentLength] = RangeLength.ToString(CultureInfo.InvariantCulture);
-            Headers[HeaderNames.ContentRange] = $"bytes {RangeStart}-{RangeEnd}/{TotalContentLength}";
-
-            if (RangeStart > 0 && SourceStream.CanSeek)
-            {
-                SourceStream.Position = RangeStart;
-            }
-        }
-
-        public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken)
-        {
-            try
-            {
-                // Headers only
-                if (IsHeadRequest)
-                {
-                    return;
-                }
-
-                using (var source = SourceStream)
-                {
-                    // If the requested range is "0-", we can optimize by just doing a stream copy
-                    if (RangeEnd >= TotalContentLength - 1)
-                    {
-                        await source.CopyToAsync(responseStream, BufferSize, cancellationToken).ConfigureAwait(false);
-                    }
-                    else
-                    {
-                        await CopyToInternalAsync(source, responseStream, RangeLength, cancellationToken).ConfigureAwait(false);
-                    }
-                }
-            }
-            finally
-            {
-                OnComplete?.Invoke();
-            }
-        }
-
-        private static async Task CopyToInternalAsync(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken)
-        {
-            var array = ArrayPool<byte>.Shared.Rent(BufferSize);
-            try
-            {
-                int bytesRead;
-                while ((bytesRead = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0)
-                {
-                    var bytesToCopy = Math.Min(bytesRead, copyLength);
-
-                    await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToCopy), cancellationToken).ConfigureAwait(false);
-
-                    copyLength -= bytesToCopy;
-
-                    if (copyLength <= 0)
-                    {
-                        break;
-                    }
-                }
-            }
-            finally
-            {
-                ArrayPool<byte>.Shared.Return(array);
-            }
-        }
-    }
-}

+ 0 - 113
Emby.Server.Implementations/HttpServer/ResponseFilter.cs

@@ -1,113 +0,0 @@
-using System;
-using System.Globalization;
-using System.Text;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Services;
-using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Logging;
-using Microsoft.Net.Http.Headers;
-
-namespace Emby.Server.Implementations.HttpServer
-{
-    /// <summary>
-    /// Class ResponseFilter.
-    /// </summary>
-    public class ResponseFilter
-    {
-        private readonly IHttpServer _server;
-        private readonly ILogger _logger;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="ResponseFilter"/> class.
-        /// </summary>
-        /// <param name="server">The HTTP server.</param>
-        /// <param name="logger">The logger.</param>
-        public ResponseFilter(IHttpServer server, ILogger logger)
-        {
-            _server = server;
-            _logger = logger;
-        }
-
-        /// <summary>
-        /// Filters the response.
-        /// </summary>
-        /// <param name="req">The req.</param>
-        /// <param name="res">The res.</param>
-        /// <param name="dto">The dto.</param>
-        public void FilterResponse(IRequest req, HttpResponse res, object dto)
-        {
-            foreach(var (key, value) in _server.GetDefaultCorsHeaders(req))
-            {
-                res.Headers.Add(key, value);
-            }
-            // Try to prevent compatibility view
-            res.Headers["Access-Control-Allow-Headers"] = "Accept, Accept-Language, Authorization, Cache-Control, " +
-                "Content-Disposition, Content-Encoding, Content-Language, Content-Length, Content-MD5, Content-Range, " +
-                "Content-Type, Cookie, Date, Host, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, " +
-                "Origin, OriginToken, Pragma, Range, Slug, Transfer-Encoding, Want-Digest, X-MediaBrowser-Token, " +
-                "X-Emby-Authorization";
-
-            if (dto is Exception exception)
-            {
-                _logger.LogError(exception, "Error processing request for {RawUrl}", req.RawUrl);
-
-                if (!string.IsNullOrEmpty(exception.Message))
-                {
-                    var error = exception.Message.Replace(Environment.NewLine, " ", StringComparison.Ordinal);
-                    error = RemoveControlCharacters(error);
-
-                    res.Headers.Add("X-Application-Error-Code", error);
-                }
-            }
-
-            if (dto is IHasHeaders hasHeaders)
-            {
-                if (!hasHeaders.Headers.ContainsKey(HeaderNames.Server))
-                {
-                    hasHeaders.Headers[HeaderNames.Server] = "Microsoft-NetCore/2.0, UPnP/1.0 DLNADOC/1.50";
-                }
-
-                // Content length has to be explicitly set on on HttpListenerResponse or it won't be happy
-                if (hasHeaders.Headers.TryGetValue(HeaderNames.ContentLength, out string contentLength)
-                    && !string.IsNullOrEmpty(contentLength))
-                {
-                    var length = long.Parse(contentLength, CultureInfo.InvariantCulture);
-
-                    if (length > 0)
-                    {
-                        res.ContentLength = length;
-                    }
-                }
-            }
-        }
-
-        /// <summary>
-        /// Removes the control characters.
-        /// </summary>
-        /// <param name="inString">The in string.</param>
-        /// <returns>System.String.</returns>
-        public static string RemoveControlCharacters(string inString)
-        {
-            if (inString == null)
-            {
-                return null;
-            }
-            else if (inString.Length == 0)
-            {
-                return inString;
-            }
-
-            var newString = new StringBuilder(inString.Length);
-
-            foreach (var ch in inString)
-            {
-                if (!char.IsControl(ch))
-                {
-                    newString.Append(ch);
-                }
-            }
-
-            return newString.ToString();
-        }
-    }
-}

+ 1 - 212
Emby.Server.Implementations/HttpServer/Security/AuthService.cs

@@ -1,17 +1,7 @@
 #pragma warning disable CS1591
 
-using System;
-using System.Linq;
-using Emby.Server.Implementations.SocketSharp;
-using Jellyfin.Data.Entities;
 using Jellyfin.Data.Enums;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Authentication;
-using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Net;
-using MediaBrowser.Controller.Security;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Services;
 using Microsoft.AspNetCore.Http;
 
 namespace Emby.Server.Implementations.HttpServer.Security
@@ -19,32 +9,11 @@ namespace Emby.Server.Implementations.HttpServer.Security
     public class AuthService : IAuthService
     {
         private readonly IAuthorizationContext _authorizationContext;
-        private readonly ISessionManager _sessionManager;
-        private readonly IServerConfigurationManager _config;
-        private readonly INetworkManager _networkManager;
 
         public AuthService(
-            IAuthorizationContext authorizationContext,
-            IServerConfigurationManager config,
-            ISessionManager sessionManager,
-            INetworkManager networkManager)
+            IAuthorizationContext authorizationContext)
         {
             _authorizationContext = authorizationContext;
-            _config = config;
-            _sessionManager = sessionManager;
-            _networkManager = networkManager;
-        }
-
-        public void Authenticate(IRequest request, IAuthenticationAttributes authAttributes)
-        {
-            ValidateUser(request, authAttributes);
-        }
-
-        public User Authenticate(HttpRequest request, IAuthenticationAttributes authAttributes)
-        {
-            var req = new WebSocketSharpRequest(request, null, request.Path);
-            var user = ValidateUser(req, authAttributes);
-            return user;
         }
 
         public AuthorizationInfo Authenticate(HttpRequest request)
@@ -62,185 +31,5 @@ namespace Emby.Server.Implementations.HttpServer.Security
 
             return auth;
         }
-
-        private User ValidateUser(IRequest request, IAuthenticationAttributes authAttributes)
-        {
-            // This code is executed before the service
-            var auth = _authorizationContext.GetAuthorizationInfo(request);
-
-            if (!IsExemptFromAuthenticationToken(authAttributes, request))
-            {
-                ValidateSecurityToken(request, auth.Token);
-            }
-
-            if (authAttributes.AllowLocalOnly && !request.IsLocal)
-            {
-                throw new SecurityException("Operation not found.");
-            }
-
-            var user = auth.User;
-
-            if (user == null && auth.UserId != Guid.Empty)
-            {
-                throw new AuthenticationException("User with Id " + auth.UserId + " not found");
-            }
-
-            if (user != null)
-            {
-                ValidateUserAccess(user, request, authAttributes);
-            }
-
-            var info = GetTokenInfo(request);
-
-            if (!IsExemptFromRoles(auth, authAttributes, request, info))
-            {
-                var roles = authAttributes.GetRoles();
-
-                ValidateRoles(roles, user);
-            }
-
-            if (!string.IsNullOrEmpty(auth.DeviceId) &&
-                !string.IsNullOrEmpty(auth.Client) &&
-                !string.IsNullOrEmpty(auth.Device))
-            {
-                _sessionManager.LogSessionActivity(
-                    auth.Client,
-                    auth.Version,
-                    auth.DeviceId,
-                    auth.Device,
-                    request.RemoteIp,
-                    user);
-            }
-
-            return user;
-        }
-
-        private void ValidateUserAccess(
-            User user,
-            IRequest request,
-            IAuthenticationAttributes authAttributes)
-        {
-            if (user.HasPermission(PermissionKind.IsDisabled))
-            {
-                throw new SecurityException("User account has been disabled.");
-            }
-
-            if (!user.HasPermission(PermissionKind.EnableRemoteAccess) && !_networkManager.IsInLocalNetwork(request.RemoteIp))
-            {
-                throw new SecurityException("User account has been disabled.");
-            }
-
-            if (!user.HasPermission(PermissionKind.IsAdministrator)
-                && !authAttributes.EscapeParentalControl
-                && !user.IsParentalScheduleAllowed())
-            {
-                request.Response.Headers.Add("X-Application-Error-Code", "ParentalControl");
-
-                throw new SecurityException("This user account is not allowed access at this time.");
-            }
-        }
-
-        private bool IsExemptFromAuthenticationToken(IAuthenticationAttributes authAttribtues, IRequest request)
-        {
-            if (!_config.Configuration.IsStartupWizardCompleted && authAttribtues.AllowBeforeStartupWizard)
-            {
-                return true;
-            }
-
-            if (authAttribtues.AllowLocal && request.IsLocal)
-            {
-                return true;
-            }
-
-            if (authAttribtues.AllowLocalOnly && request.IsLocal)
-            {
-                return true;
-            }
-
-            if (authAttribtues.IgnoreLegacyAuth)
-            {
-                return true;
-            }
-
-            return false;
-        }
-
-        private bool IsExemptFromRoles(AuthorizationInfo auth, IAuthenticationAttributes authAttribtues, IRequest request, AuthenticationInfo tokenInfo)
-        {
-            if (!_config.Configuration.IsStartupWizardCompleted && authAttribtues.AllowBeforeStartupWizard)
-            {
-                return true;
-            }
-
-            if (authAttribtues.AllowLocal && request.IsLocal)
-            {
-                return true;
-            }
-
-            if (authAttribtues.AllowLocalOnly && request.IsLocal)
-            {
-                return true;
-            }
-
-            if (string.IsNullOrEmpty(auth.Token))
-            {
-                return true;
-            }
-
-            if (tokenInfo != null && tokenInfo.UserId.Equals(Guid.Empty))
-            {
-                return true;
-            }
-
-            return false;
-        }
-
-        private static void ValidateRoles(string[] roles, User user)
-        {
-            if (roles.Contains("admin", StringComparer.OrdinalIgnoreCase))
-            {
-                if (user == null || !user.HasPermission(PermissionKind.IsAdministrator))
-                {
-                    throw new SecurityException("User does not have admin access.");
-                }
-            }
-
-            if (roles.Contains("delete", StringComparer.OrdinalIgnoreCase))
-            {
-                if (user == null || !user.HasPermission(PermissionKind.EnableContentDeletion))
-                {
-                    throw new SecurityException("User does not have delete access.");
-                }
-            }
-
-            if (roles.Contains("download", StringComparer.OrdinalIgnoreCase))
-            {
-                if (user == null || !user.HasPermission(PermissionKind.EnableContentDownloading))
-                {
-                    throw new SecurityException("User does not have download access.");
-                }
-            }
-        }
-
-        private static AuthenticationInfo GetTokenInfo(IRequest request)
-        {
-            request.Items.TryGetValue("OriginalAuthenticationInfo", out var info);
-            return info as AuthenticationInfo;
-        }
-
-        private void ValidateSecurityToken(IRequest request, string token)
-        {
-            if (string.IsNullOrEmpty(token))
-            {
-                throw new AuthenticationException("Access token is required.");
-            }
-
-            var info = GetTokenInfo(request);
-
-            if (info == null)
-            {
-                throw new AuthenticationException("Access token is invalid or expired.");
-            }
-        }
     }
 }

+ 10 - 11
Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs

@@ -7,7 +7,6 @@ using System.Net;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Security;
-using MediaBrowser.Model.Services;
 using Microsoft.AspNetCore.Http;
 using Microsoft.Net.Http.Headers;
 
@@ -26,12 +25,12 @@ namespace Emby.Server.Implementations.HttpServer.Security
 
         public AuthorizationInfo GetAuthorizationInfo(object requestContext)
         {
-            return GetAuthorizationInfo((IRequest)requestContext);
+            return GetAuthorizationInfo((HttpContext)requestContext);
         }
 
-        public AuthorizationInfo GetAuthorizationInfo(IRequest requestContext)
+        public AuthorizationInfo GetAuthorizationInfo(HttpContext requestContext)
         {
-            if (requestContext.Items.TryGetValue("AuthorizationInfo", out var cached))
+            if (requestContext.Request.HttpContext.Items.TryGetValue("AuthorizationInfo", out var cached))
             {
                 return (AuthorizationInfo)cached;
             }
@@ -52,18 +51,18 @@ namespace Emby.Server.Implementations.HttpServer.Security
         /// </summary>
         /// <param name="httpReq">The HTTP req.</param>
         /// <returns>Dictionary{System.StringSystem.String}.</returns>
-        private AuthorizationInfo GetAuthorization(IRequest httpReq)
+        private AuthorizationInfo GetAuthorization(HttpContext httpReq)
         {
             var auth = GetAuthorizationDictionary(httpReq);
             var (authInfo, originalAuthInfo) =
-                GetAuthorizationInfoFromDictionary(auth, httpReq.Headers, httpReq.QueryString);
+                GetAuthorizationInfoFromDictionary(auth, httpReq.Request.Headers, httpReq.Request.Query);
 
             if (originalAuthInfo != null)
             {
-                httpReq.Items["OriginalAuthenticationInfo"] = originalAuthInfo;
+                httpReq.Request.HttpContext.Items["OriginalAuthenticationInfo"] = originalAuthInfo;
             }
 
-            httpReq.Items["AuthorizationInfo"] = authInfo;
+            httpReq.Request.HttpContext.Items["AuthorizationInfo"] = authInfo;
             return authInfo;
         }
 
@@ -203,13 +202,13 @@ namespace Emby.Server.Implementations.HttpServer.Security
         /// </summary>
         /// <param name="httpReq">The HTTP req.</param>
         /// <returns>Dictionary{System.StringSystem.String}.</returns>
-        private Dictionary<string, string> GetAuthorizationDictionary(IRequest httpReq)
+        private Dictionary<string, string> GetAuthorizationDictionary(HttpContext httpReq)
         {
-            var auth = httpReq.Headers["X-Emby-Authorization"];
+            var auth = httpReq.Request.Headers["X-Emby-Authorization"];
 
             if (string.IsNullOrEmpty(auth))
             {
-                auth = httpReq.Headers[HeaderNames.Authorization];
+                auth = httpReq.Request.Headers[HeaderNames.Authorization];
             }
 
             return GetAuthorization(auth);

+ 7 - 13
Emby.Server.Implementations/HttpServer/Security/SessionContext.cs

@@ -2,11 +2,11 @@
 
 using System;
 using Jellyfin.Data.Entities;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Net;
-using MediaBrowser.Controller.Security;
 using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Services;
+using Microsoft.AspNetCore.Http;
 
 namespace Emby.Server.Implementations.HttpServer.Security
 {
@@ -23,26 +23,20 @@ namespace Emby.Server.Implementations.HttpServer.Security
             _sessionManager = sessionManager;
         }
 
-        public SessionInfo GetSession(IRequest requestContext)
+        public SessionInfo GetSession(HttpContext requestContext)
         {
             var authorization = _authContext.GetAuthorizationInfo(requestContext);
 
             var user = authorization.User;
-            return _sessionManager.LogSessionActivity(authorization.Client, authorization.Version, authorization.DeviceId, authorization.Device, requestContext.RemoteIp, user);
-        }
-
-        private AuthenticationInfo GetTokenInfo(IRequest request)
-        {
-            request.Items.TryGetValue("OriginalAuthenticationInfo", out var info);
-            return info as AuthenticationInfo;
+            return _sessionManager.LogSessionActivity(authorization.Client, authorization.Version, authorization.DeviceId, authorization.Device, requestContext.Request.RemoteIp(), user);
         }
 
         public SessionInfo GetSession(object requestContext)
         {
-            return GetSession((IRequest)requestContext);
+            return GetSession((HttpContext)requestContext);
         }
 
-        public User GetUser(IRequest requestContext)
+        public User GetUser(HttpContext requestContext)
         {
             var session = GetSession(requestContext);
 
@@ -51,7 +45,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
 
         public User GetUser(object requestContext)
         {
-            return GetUser((IRequest)requestContext);
+            return GetUser((HttpContext)requestContext);
         }
     }
 }

+ 0 - 120
Emby.Server.Implementations/HttpServer/StreamWriter.cs

@@ -1,120 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Services;
-using Microsoft.Net.Http.Headers;
-
-namespace Emby.Server.Implementations.HttpServer
-{
-    /// <summary>
-    /// Class StreamWriter.
-    /// </summary>
-    public class StreamWriter : IAsyncStreamWriter, IHasHeaders
-    {
-        /// <summary>
-        /// The options.
-        /// </summary>
-        private readonly IDictionary<string, string> _options = new Dictionary<string, string>();
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="StreamWriter" /> class.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="contentType">Type of the content.</param>
-        public StreamWriter(Stream source, string contentType)
-        {
-            if (string.IsNullOrEmpty(contentType))
-            {
-                throw new ArgumentNullException(nameof(contentType));
-            }
-
-            SourceStream = source;
-
-            Headers["Content-Type"] = contentType;
-
-            if (source.CanSeek)
-            {
-                Headers[HeaderNames.ContentLength] = source.Length.ToString(CultureInfo.InvariantCulture);
-            }
-
-            Headers[HeaderNames.ContentType] = contentType;
-        }
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="StreamWriter"/> class.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="contentType">Type of the content.</param>
-        /// <param name="contentLength">The content length.</param>
-        public StreamWriter(byte[] source, string contentType, int contentLength)
-        {
-            if (string.IsNullOrEmpty(contentType))
-            {
-                throw new ArgumentNullException(nameof(contentType));
-            }
-
-            SourceBytes = source;
-
-            Headers[HeaderNames.ContentLength] = contentLength.ToString(CultureInfo.InvariantCulture);
-            Headers[HeaderNames.ContentType] = contentType;
-        }
-
-        /// <summary>
-        /// Gets or sets the source stream.
-        /// </summary>
-        /// <value>The source stream.</value>
-        private Stream SourceStream { get; set; }
-
-        private byte[] SourceBytes { get; set; }
-
-        /// <summary>
-        /// Gets the options.
-        /// </summary>
-        /// <value>The options.</value>
-        public IDictionary<string, string> Headers => _options;
-
-        /// <summary>
-        /// Fires when complete.
-        /// </summary>
-        public Action OnComplete { get; set; }
-
-        /// <summary>
-        /// Fires when an error occours.
-        /// </summary>
-        public Action OnError { get; set; }
-
-        /// <inheritdoc />
-        public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken)
-        {
-            try
-            {
-                var bytes = SourceBytes;
-
-                if (bytes != null)
-                {
-                    await responseStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken).ConfigureAwait(false);
-                }
-                else
-                {
-                    using (var src = SourceStream)
-                    {
-                        await src.CopyToAsync(responseStream, cancellationToken).ConfigureAwait(false);
-                    }
-                }
-            }
-            catch
-            {
-                OnError?.Invoke();
-
-                throw;
-            }
-            finally
-            {
-                OnComplete?.Invoke();
-            }
-        }
-    }
-}

+ 0 - 64
Emby.Server.Implementations/Services/HttpResult.cs

@@ -1,64 +0,0 @@
-#pragma warning disable CS1591
-
-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 HttpResult(object response, string contentType, HttpStatusCode statusCode)
-        {
-            this.Headers = new Dictionary<string, string>();
-
-            this.Response = response;
-            this.ContentType = contentType;
-            this.StatusCode = statusCode;
-        }
-
-        public object Response { get; set; }
-
-        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?.Response;
-
-            if (this.Response is byte[] bytesResponse)
-            {
-                var contentLength = bytesResponse.Length;
-
-                if (response != null)
-                {
-                    response.ContentLength = contentLength;
-                }
-
-                if (contentLength > 0)
-                {
-                    await responseStream.WriteAsync(bytesResponse, 0, contentLength, cancellationToken).ConfigureAwait(false);
-                }
-
-                return;
-            }
-
-            await ResponseHelper.WriteObject(this.RequestContext, this.Response, response).ConfigureAwait(false);
-        }
-    }
-}

+ 0 - 51
Emby.Server.Implementations/Services/RequestHelper.cs

@@ -1,51 +0,0 @@
-#pragma warning disable CS1591
-
-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?.Split(';')[0].ToLowerInvariant().Trim();
-        }
-    }
-}

+ 0 - 141
Emby.Server.Implementations/Services/ResponseHelper.cs

@@ -1,141 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Globalization;
-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;
-using Microsoft.AspNetCore.Http;
-
-namespace Emby.Server.Implementations.Services
-{
-    public static class ResponseHelper
-    {
-        public static Task WriteToResponse(HttpResponse response, IRequest request, object result, CancellationToken cancellationToken)
-        {
-            if (result == null)
-            {
-                if (response.StatusCode == (int)HttpStatusCode.OK)
-                {
-                    response.StatusCode = (int)HttpStatusCode.NoContent;
-                }
-
-                response.ContentLength = 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;
-            }
-
-            if (result is IHasHeaders responseOptions)
-            {
-                foreach (var responseHeaders in responseOptions.Headers)
-                {
-                    if (string.Equals(responseHeaders.Key, "Content-Length", StringComparison.OrdinalIgnoreCase))
-                    {
-                        response.ContentLength = long.Parse(responseHeaders.Value, CultureInfo.InvariantCulture);
-                        continue;
-                    }
-
-                    response.Headers.Add(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";
-            }
-
-            switch (result)
-            {
-                case IAsyncStreamWriter asyncStreamWriter:
-                    return asyncStreamWriter.WriteToAsync(response.Body, cancellationToken);
-                case IStreamWriter streamWriter:
-                    streamWriter.WriteTo(response.Body);
-                    return Task.CompletedTask;
-                case FileWriter fileWriter:
-                    return fileWriter.WriteToAsync(response, cancellationToken);
-                case Stream stream:
-                    return CopyStream(stream, response.Body);
-                case byte[] bytes:
-                    response.ContentType = "application/octet-stream";
-                    response.ContentLength = bytes.Length;
-
-                    if (bytes.Length > 0)
-                    {
-                        return response.Body.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
-                    }
-
-                    return Task.CompletedTask;
-                case string responseText:
-                    var responseTextAsBytes = Encoding.UTF8.GetBytes(responseText);
-                    response.ContentLength = responseTextAsBytes.Length;
-
-                    if (responseTextAsBytes.Length > 0)
-                    {
-                        return response.Body.WriteAsync(responseTextAsBytes, 0, responseTextAsBytes.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, HttpResponse 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.ContentLength = contentLength;
-
-                if (contentLength > 0)
-                {
-                    await ms.CopyToAsync(response.Body).ConfigureAwait(false);
-                }
-            }
-        }
-    }
-}

+ 0 - 202
Emby.Server.Implementations/Services/ServiceController.cs

@@ -1,202 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Threading.Tasks;
-using Emby.Server.Implementations.HttpServer;
-using MediaBrowser.Model.Services;
-using Microsoft.Extensions.Logging;
-
-namespace Emby.Server.Implementations.Services
-{
-    public delegate object ActionInvokerFn(object intance, object request);
-
-    public delegate void VoidActionInvokerFn(object intance, object request);
-
-    public class ServiceController
-    {
-        private readonly ILogger<ServiceController> _logger;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="ServiceController"/> class.
-        /// </summary>
-        /// <param name="logger">The <see cref="ServiceController"/> logger.</param>
-        public ServiceController(ILogger<ServiceController> logger)
-        {
-            _logger = logger;
-        }
-
-        public void Init(HttpListenerHost appHost, IEnumerable<Type> serviceTypes)
-        {
-            foreach (var serviceType in serviceTypes)
-            {
-                RegisterService(appHost, serviceType);
-            }
-        }
-
-        public void RegisterService(HttpListenerHost appHost, Type serviceType)
-        {
-            // Make sure the provided type implements IService
-            if (!typeof(IService).IsAssignableFrom(serviceType))
-            {
-                _logger.LogWarning("Tried to register a service that does not implement IService: {ServiceType}", serviceType);
-                return;
-            }
-
-            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 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[0] != '/')
-            {
-                throw new ArgumentException(
-                    string.Format(
-                        CultureInfo.InvariantCulture,
-                        "Route '{0}' on '{1}' must start with a '/'",
-                        restPath.Path,
-                        restPath.RequestType.GetMethodName()));
-            }
-
-            if (restPath.Path.IndexOfAny(InvalidRouteChars) != -1)
-            {
-                throw new ArgumentException(
-                    string.Format(
-                        CultureInfo.InvariantCulture,
-                        "Route '{0}' on '{1}' contains invalid chars. ",
-                        restPath.Path,
-                        restPath.RequestType.GetMethodName()));
-            }
-
-            if (RestPathMap.TryGetValue(restPath.FirstMatchHashKey, out List<RestPath> pathsAtFirstMatch))
-            {
-                pathsAtFirstMatch.Add(restPath);
-            }
-            else
-            {
-                RestPathMap[restPath.FirstMatchHashKey] = new List<RestPath>() { 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 httpHost, object requestDto, IRequest req)
-        {
-            var requestType = requestDto.GetType();
-            req.OperationName = requestType.Name;
-
-            var serviceType = httpHost.GetServiceTypeByRequest(requestType);
-
-            var service = httpHost.CreateInstance(serviceType);
-
-            if (service is IRequiresRequest serviceRequiresContext)
-            {
-                serviceRequiresContext.Request = req;
-            }
-
-            // Executes the service and returns the result
-            return ServiceExecGeneral.Execute(serviceType, req, service, requestDto, requestType.GetMethodName());
-        }
-    }
-}

+ 0 - 230
Emby.Server.Implementations/Services/ServiceExec.cs

@@ -1,230 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-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.HasStarted)
-                        {
-                            Task.FromResult<object>(null);
-                        }
-                    }
-                }
-
-                var response = actionContext.ServiceAction(instance, requestDto);
-
-                if (response is Task taskResponse)
-                {
-                    return GetTaskResult(taskResponse);
-                }
-
-                return Task.FromResult(response);
-            }
-
-            var expectedMethodName = actionName.Substring(0, 1) + actionName.Substring(1).ToLowerInvariant();
-            throw new NotImplementedException(
-                string.Format(
-                    CultureInfo.InvariantCulture,
-                    "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
-            {
-                if (task is Task<object> taskObject)
-                {
-                    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())
-                };
-
-                actionCtx.ServiceAction = CreateExecFn(serviceType, requestType, mi);
-
-                var reqFilters = new List<IHasRequestFilter>();
-
-                foreach (var attr in mi.GetCustomAttributes(true))
-                {
-                    if (attr is IHasRequestFilter hasReqFilter)
-                    {
-                        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;
-                };
-            }
-        }
-    }
-}

+ 0 - 212
Emby.Server.Implementations/Services/ServiceHandler.cs

@@ -1,212 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.Net.Mime;
-using System.Reflection;
-using System.Threading;
-using System.Threading.Tasks;
-using Emby.Server.Implementations.HttpServer;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Model.Services;
-using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Logging;
-
-namespace Emby.Server.Implementations.Services
-{
-    public class ServiceHandler
-    {
-        private RestPath _restPath;
-
-        private string _responseContentType;
-
-        internal ServiceHandler(RestPath restPath, string responseContentType)
-        {
-            _restPath = restPath;
-            _responseContentType = responseContentType;
-        }
-
-        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.Invoke(requestType, httpReq.InputStream);
-                }
-            }
-
-            return Task.FromResult(host.CreateInstance(requestType));
-        }
-
-        public static string GetSanitizedPathInfo(string pathInfo, out string contentType)
-        {
-            contentType = null;
-            var pos = pathInfo.LastIndexOf('.');
-            if (pos != -1)
-            {
-                var format = pathInfo.AsSpan().Slice(pos + 1);
-                contentType = GetFormatContentType(format);
-                if (contentType != null)
-                {
-                    pathInfo = pathInfo.Substring(0, pos);
-                }
-            }
-
-            return pathInfo;
-        }
-
-        private static string GetFormatContentType(ReadOnlySpan<char> format)
-        {
-            if (format.Equals("json", StringComparison.Ordinal))
-            {
-                return MediaTypeNames.Application.Json;
-            }
-            else if (format.Equals("xml", StringComparison.Ordinal))
-            {
-                return MediaTypeNames.Application.Xml;
-            }
-
-            return null;
-        }
-
-        public async Task ProcessRequestAsync(HttpListenerHost httpHost, IRequest httpReq, HttpResponse httpRes, CancellationToken cancellationToken)
-        {
-            httpReq.Items["__route"] = _restPath;
-
-            if (_responseContentType != null)
-            {
-                httpReq.ResponseContentType = _responseContentType;
-            }
-
-            var request = await CreateRequest(httpHost, httpReq, _restPath).ConfigureAwait(false);
-
-            httpHost.ApplyRequestFilters(httpReq, httpRes, request);
-
-            httpRes.HttpContext.SetServiceStackRequest(httpReq);
-            var response = await httpHost.ServiceController.Execute(httpHost, request, httpReq).ConfigureAwait(false);
-
-            // Apply response filters
-            foreach (var responseFilter in httpHost.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)
-        {
-            var requestType = restPath.RequestType;
-
-            if (RequireqRequestStream(requestType))
-            {
-                // Used by IRequiresRequestStream
-                var requestParams = GetRequestParams(httpReq.Response.HttpContext.Request);
-                var request = ServiceHandler.CreateRequest(httpReq, restPath, requestParams, host.CreateInstance(requestType));
-
-                var rawReq = (IRequiresRequestStream)request;
-                rawReq.RequestStream = httpReq.InputStream;
-                return rawReq;
-            }
-            else
-            {
-                var requestParams = GetFlattenedRequestParams(httpReq.Response.HttpContext.Request);
-
-                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 _)
-                : httpReq.PathInfo;
-
-            return restPath.CreateRequest(pathInfo, requestParams, requestDto);
-        }
-
-        /// <summary>
-        /// Duplicate Params are given a unique key by appending a #1 suffix
-        /// </summary>
-        private static Dictionary<string, string> GetRequestParams(HttpRequest request)
-        {
-            var map = new Dictionary<string, string>();
-
-            foreach (var pair in request.Query)
-            {
-                var values = pair.Value;
-                if (values.Count == 1)
-                {
-                    map[pair.Key] = values[0];
-                }
-                else
-                {
-                    for (var i = 0; i < values.Count; i++)
-                    {
-                        map[pair.Key + (i == 0 ? string.Empty : "#" + i)] = values[i];
-                    }
-                }
-            }
-
-            if ((IsMethod(request.Method, "POST") || IsMethod(request.Method, "PUT"))
-                && request.HasFormContentType)
-            {
-                foreach (var pair in request.Form)
-                {
-                    var values = pair.Value;
-                    if (values.Count == 1)
-                    {
-                        map[pair.Key] = values[0];
-                    }
-                    else
-                    {
-                        for (var i = 0; i < values.Count; i++)
-                        {
-                            map[pair.Key + (i == 0 ? string.Empty : "#" + i)] = values[i];
-                        }
-                    }
-                }
-            }
-
-            return map;
-        }
-
-        private static bool IsMethod(string method, string expected)
-            => string.Equals(method, expected, StringComparison.OrdinalIgnoreCase);
-
-        /// <summary>
-        /// Duplicate params have their values joined together in a comma-delimited string.
-        /// </summary>
-        private static Dictionary<string, string> GetFlattenedRequestParams(HttpRequest request)
-        {
-            var map = new Dictionary<string, string>();
-
-            foreach (var pair in request.Query)
-            {
-                map[pair.Key] = pair.Value;
-            }
-
-            if ((IsMethod(request.Method, "POST") || IsMethod(request.Method, "PUT"))
-                && request.HasFormContentType)
-            {
-                foreach (var pair in request.Form)
-                {
-                    map[pair.Key] = pair.Value;
-                }
-            }
-
-            return map;
-        }
-    }
-}

+ 0 - 20
Emby.Server.Implementations/Services/ServiceMethod.cs

@@ -1,20 +0,0 @@
-#pragma warning disable CS1591
-
-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;
-        }
-    }
-}

+ 0 - 550
Emby.Server.Implementations/Services/ServicePath.cs

@@ -1,550 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Reflection;
-using System.Text;
-using System.Text.Json.Serialization;
-
-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 = "{";
-
-        private 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;
-
-        internal static string[] IgnoreAttributesNamed = new[]
-        {
-            nameof(JsonIgnoreAttribute)
-        };
-
-        private static Type _excludeType = typeof(Stream);
-
-        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>
-        /// Gets or sets 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 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);
-
-                if (part.IndexOf(ComponentSeperator, StringComparison.Ordinal) == -1)
-                {
-                    continue;
-                }
-
-                var subParts = part.Split(ComponentSeperator);
-                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 (component.IndexOf(VariablePrefix, StringComparison.OrdinalIgnoreCase) != -1
-                    && component.IndexOf(ComponentSeperator, StringComparison.Ordinal) != -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, StringComparison.Ordinal))
-                {
-                    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.Length;
-            this.IsWildCardPath = this.wildcardCount > 0;
-
-            this.FirstMatchHashKey = !this.IsWildCardPath
-                ? this.PathComponentsCount + PathSeperator + firstLiteralMatch
-                : WildCardChar + PathSeperator + firstLiteralMatch;
-
-            this.typeDeserializer = new StringMapTypeDeserializer(createInstanceFn, getParseFn, this.RequestType);
-
-            _propertyNamesMap = new HashSet<string>(
-                    GetSerializableProperties(RequestType).Select(x => x.Name),
-                    StringComparer.OrdinalIgnoreCase);
-        }
-
-        internal static IEnumerable<PropertyInfo> GetSerializableProperties(Type type)
-        {
-            foreach (var prop in GetPublicProperties(type))
-            {
-                if (prop.GetMethod == null
-                    || _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)
-                {
-                    yield return prop;
-                }
-            }
-        }
-
-        private static IEnumerable<PropertyInfo> GetPublicProperties(Type type)
-        {
-            if (type.IsInterface)
-            {
-                var propertyInfos = new List<PropertyInfo>();
-                var considered = new List<Type>()
-                {
-                    type
-                };
-                var queue = new Queue<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 newPropertyInfos = GetTypesPublicProperties(subType)
-                        .Where(x => !propertyInfos.Contains(x));
-
-                    propertyInfos.InsertRange(0, newPropertyInfos);
-                }
-
-                return propertyInfos;
-            }
-
-            return GetTypesPublicProperties(type)
-                .Where(x => x.GetIndexParameters().Length == 0);
-        }
-
-        private static IEnumerable<PropertyInfo> GetTypesPublicProperties(Type subType)
-        {
-            foreach (var pi in subType.GetRuntimeProperties())
-            {
-                var mi = pi.GetMethod ?? pi.SetMethod;
-                if (mi != null && mi.IsStatic)
-                {
-                    continue;
-                }
-
-                yield return pi;
-            }
-        }
-
-        /// <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 HashSet<string> _propertyNamesMap;
-
-        public int MatchScore(string httpMethod, string[] withPathInfoParts)
-        {
-            var isMatch = IsMatch(httpMethod, withPathInfoParts, out var wildcardMatchCount);
-            if (!isMatch)
-            {
-                return -1;
-            }
-
-            // Routes with least wildcard matches get the highest score
-            var score = Math.Max(100 - wildcardMatchCount, 1) * 1000
-                        // Routes with less variable (and more literal) matches
-                        + 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;
-        }
-
-        /// <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
-                            && !string.Equals(withPathInfoParts[pathIx], this.literalsToMatch[i + 1], StringComparison.InvariantCultureIgnoreCase))
-                        {
-                            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
-                        || !string.Equals(withPathInfoParts[pathIx], literalToMatch, StringComparison.InvariantCultureIgnoreCase))
-                    {
-                        return false;
-                    }
-
-                    pathIx++;
-                }
-            }
-
-            return pathIx == withPathInfoParts.Length;
-        }
-
-        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(
-                            CultureInfo.InvariantCulture,
-                            "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.Contains(variableName))
-                {
-                    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)
-                                .Append(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(value);
-                            pathIx++;
-                            while (!string.Equals(requestComponents[pathIx], stopLiteral, StringComparison.OrdinalIgnoreCase))
-                            {
-                                sb.Append(PathSeperatorChar)
-                                    .Append(requestComponents[pathIx++]);
-                            }
-
-                            value = sb.ToString();
-                        }
-                        else
-                        {
-                            value = null;
-                        }
-                    }
-                }
-                else
-                {
-                    // Variable consumes single path item
-                    pathIx++;
-                }
-
-                requestKeyValuesMap[variableName] = 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)
-            {
-            }
-        }
-    }
-}

+ 0 - 118
Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs

@@ -1,118 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.Reflection;
-using MediaBrowser.Common.Extensions;
-
-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, Type propertyType)
-            {
-                PropertySetFn = propertySetFn;
-                PropertyParseStringFn = propertyParseStringFn;
-                PropertyType = propertyType;
-            }
-
-            public Action<object, object> PropertySetFn { get; private set; }
-
-            public Func<string, object> PropertyParseStringFn { get; private set; }
-
-            public Type PropertyType { get; private set; }
-        }
-
-        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(propertyInfo);
-                var propertyType = propertyInfo.PropertyType;
-                var propertyParseStringFn = GetParseFn(propertyType);
-                var propertySerializer = new PropertySerializerEntry(propertySetFn, propertyParseStringFn, propertyType);
-
-                propertySetterMap[propertyInfo.Name] = propertySerializer;
-            }
-        }
-
-        public object PopulateFromMap(object instance, IDictionary<string, string> keyValuePairs)
-        {
-            PropertySerializerEntry propertySerializerEntry = null;
-
-            if (instance == null)
-            {
-                instance = _CreateInstanceFn(type);
-            }
-
-            foreach (var pair in keyValuePairs)
-            {
-                string propertyName = pair.Key;
-                string propertyTextValue = pair.Value;
-
-                if (propertyTextValue == null
-                    || !propertySetterMap.TryGetValue(propertyName, out propertySerializerEntry)
-                    || 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 = StringExtensions.LeftPart(propertyTextValue, ',').ToString();
-                }
-
-                var value = propertySerializerEntry.PropertyParseStringFn(propertyTextValue);
-                if (value == null)
-                {
-                    continue;
-                }
-
-                propertySerializerEntry.PropertySetFn(instance, value);
-            }
-
-            return instance;
-        }
-    }
-
-    internal static class TypeAccessor
-    {
-        public static Action<object, object> GetSetPropertyMethod(PropertyInfo propertyInfo)
-        {
-            if (!propertyInfo.CanWrite || propertyInfo.GetIndexParameters().Length > 0)
-            {
-                return null;
-            }
-
-            var setMethodInfo = propertyInfo.SetMethod;
-            return (instance, value) => setMethodInfo.Invoke(instance, new[] { value });
-        }
-    }
-}

+ 0 - 27
Emby.Server.Implementations/Services/UrlExtensions.cs

@@ -1,27 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using MediaBrowser.Common.Extensions;
-
-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
-                ? StringExtensions.LeftPart(type.FullName, "[[", StringComparison.Ordinal).ToString() // Generic Fullname
-                    .Replace(type.Namespace + ".", string.Empty, StringComparison.Ordinal) // Trim Namespaces
-                    .Replace("+", ".", StringComparison.Ordinal) // Convert nested into normal type
-                : type.Name;
-
-            return type.IsGenericParameter ? "'" + typeName : typeName;
-        }
-    }
-}

+ 0 - 248
Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs

@@ -1,248 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Net;
-using System.Net.Mime;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Net;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Http.Extensions;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Primitives;
-using Microsoft.Net.Http.Headers;
-using IHttpRequest = MediaBrowser.Model.Services.IHttpRequest;
-
-namespace Emby.Server.Implementations.SocketSharp
-{
-    public class WebSocketSharpRequest : IHttpRequest
-    {
-        private const string FormUrlEncoded = "application/x-www-form-urlencoded";
-        private const string MultiPartFormData = "multipart/form-data";
-        private const string Soap11 = "text/xml; charset=utf-8";
-
-        private string _remoteIp;
-        private Dictionary<string, object> _items;
-        private string _responseContentType;
-
-        public WebSocketSharpRequest(HttpRequest httpRequest, HttpResponse httpResponse, string operationName)
-        {
-            this.OperationName = operationName;
-            this.Request = httpRequest;
-            this.Response = httpResponse;
-        }
-
-        public string Accept => StringValues.IsNullOrEmpty(Request.Headers[HeaderNames.Accept]) ? null : Request.Headers[HeaderNames.Accept].ToString();
-
-        public string Authorization => StringValues.IsNullOrEmpty(Request.Headers[HeaderNames.Authorization]) ? null : Request.Headers[HeaderNames.Authorization].ToString();
-
-        public HttpRequest Request { get; }
-
-        public HttpResponse Response { get; }
-
-        public string OperationName { get; set; }
-
-        public string RawUrl => Request.GetEncodedPathAndQuery();
-
-        public string AbsoluteUri => Request.GetDisplayUrl().TrimEnd('/');
-
-        public string RemoteIp
-        {
-            get
-            {
-                if (_remoteIp != null)
-                {
-                    return _remoteIp;
-                }
-
-                IPAddress ip;
-
-                // "Real" remote ip might be in X-Forwarded-For of X-Real-Ip
-                // (if the server is behind a reverse proxy for example)
-                if (!IPAddress.TryParse(GetHeader(CustomHeaderNames.XForwardedFor), out ip))
-                {
-                    if (!IPAddress.TryParse(GetHeader(CustomHeaderNames.XRealIP), out ip))
-                    {
-                        ip = Request.HttpContext.Connection.RemoteIpAddress;
-
-                        // Default to the loopback address if no RemoteIpAddress is specified (i.e. during integration tests)
-                        ip ??= IPAddress.Loopback;
-                    }
-                }
-
-                return _remoteIp = NormalizeIp(ip).ToString();
-            }
-        }
-
-        public string[] AcceptTypes => Request.Headers.GetCommaSeparatedValues(HeaderNames.Accept);
-
-        public Dictionary<string, object> Items => _items ?? (_items = new Dictionary<string, object>());
-
-        public string ResponseContentType
-        {
-            get =>
-                _responseContentType
-                ?? (_responseContentType = GetResponseContentType(Request));
-            set => _responseContentType = value;
-        }
-
-        public string PathInfo => Request.Path.Value;
-
-        public string UserAgent => Request.Headers[HeaderNames.UserAgent];
-
-        public IHeaderDictionary Headers => Request.Headers;
-
-        public IQueryCollection QueryString => Request.Query;
-
-        public bool IsLocal =>
-            (Request.HttpContext.Connection.LocalIpAddress == null
-            && Request.HttpContext.Connection.RemoteIpAddress == null)
-            || Request.HttpContext.Connection.LocalIpAddress.Equals(Request.HttpContext.Connection.RemoteIpAddress);
-
-        public string HttpMethod => Request.Method;
-
-        public string Verb => HttpMethod;
-
-        public string ContentType => Request.ContentType;
-
-        public Uri UrlReferrer => Request.GetTypedHeaders().Referer;
-
-        public Stream InputStream => Request.Body;
-
-        public long ContentLength => Request.ContentLength ?? 0;
-
-        private string GetHeader(string name) => Request.Headers[name].ToString();
-
-        private static IPAddress NormalizeIp(IPAddress ip)
-        {
-            if (ip.IsIPv4MappedToIPv6)
-            {
-                return ip.MapToIPv4();
-            }
-
-            return ip;
-        }
-
-        public static string GetResponseContentType(HttpRequest httpReq)
-        {
-            var specifiedContentType = GetQueryStringContentType(httpReq);
-            if (!string.IsNullOrEmpty(specifiedContentType))
-            {
-                return specifiedContentType;
-            }
-
-            const string ServerDefaultContentType = MediaTypeNames.Application.Json;
-
-            var acceptContentTypes = httpReq.Headers.GetCommaSeparatedValues(HeaderNames.Accept);
-            string defaultContentType = null;
-            if (HasAnyOfContentTypes(httpReq, FormUrlEncoded, MultiPartFormData))
-            {
-                defaultContentType = ServerDefaultContentType;
-            }
-
-            var acceptsAnything = false;
-            var hasDefaultContentType = defaultContentType != null;
-            if (acceptContentTypes != null)
-            {
-                foreach (ReadOnlySpan<char> acceptsType in acceptContentTypes)
-                {
-                    ReadOnlySpan<char> contentType = acceptsType;
-                    var index = contentType.IndexOf(';');
-                    if (index != -1)
-                    {
-                        contentType = contentType.Slice(0, index);
-                    }
-
-                    contentType = contentType.Trim();
-                    acceptsAnything = contentType.Equals("*/*", StringComparison.OrdinalIgnoreCase);
-
-                    if (acceptsAnything)
-                    {
-                        break;
-                    }
-                }
-
-                if (acceptsAnything)
-                {
-                    if (hasDefaultContentType)
-                    {
-                        return defaultContentType;
-                    }
-                    else
-                    {
-                        return ServerDefaultContentType;
-                    }
-                }
-            }
-
-            if (acceptContentTypes == null && httpReq.ContentType == Soap11)
-            {
-                return Soap11;
-            }
-
-            // We could also send a '406 Not Acceptable', but this is allowed also
-            return ServerDefaultContentType;
-        }
-
-        public static bool HasAnyOfContentTypes(HttpRequest request, params string[] contentTypes)
-        {
-            if (contentTypes == null || request.ContentType == null)
-            {
-                return false;
-            }
-
-            foreach (var contentType in contentTypes)
-            {
-                if (IsContentType(request, contentType))
-                {
-                    return true;
-                }
-            }
-
-            return false;
-        }
-
-        public static bool IsContentType(HttpRequest request, string contentType)
-        {
-            return request.ContentType.StartsWith(contentType, StringComparison.OrdinalIgnoreCase);
-        }
-
-        private static string GetQueryStringContentType(HttpRequest httpReq)
-        {
-            ReadOnlySpan<char> format = httpReq.Query["format"].ToString();
-            if (format == ReadOnlySpan<char>.Empty)
-            {
-                const int FormatMaxLength = 4;
-                ReadOnlySpan<char> pi = httpReq.Path.ToString();
-                if (pi == null || pi.Length <= FormatMaxLength)
-                {
-                    return null;
-                }
-
-                if (pi[0] == '/')
-                {
-                    pi = pi.Slice(1);
-                }
-
-                format = pi.LeftPart('/');
-                if (format.Length > FormatMaxLength)
-                {
-                    return null;
-                }
-            }
-
-            format = format.LeftPart('.');
-            if (format.Contains("json", StringComparison.OrdinalIgnoreCase))
-            {
-                return "application/json";
-            }
-            else if (format.Contains("xml", StringComparison.OrdinalIgnoreCase))
-            {
-                return "application/xml";
-            }
-
-            return null;
-        }
-    }
-}

+ 42 - 13
MediaBrowser.Common/Extensions/HttpContextExtensions.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Model.Services;
+using System.Net;
+using MediaBrowser.Common.Net;
 using Microsoft.AspNetCore.Http;
 
 namespace MediaBrowser.Common.Extensions
@@ -8,26 +9,54 @@ namespace MediaBrowser.Common.Extensions
     /// </summary>
     public static class HttpContextExtensions
     {
-        private const string ServiceStackRequest = "ServiceStackRequest";
-
         /// <summary>
-        /// Set the ServiceStack request.
+        /// Checks the origin of the HTTP request.
         /// </summary>
-        /// <param name="httpContext">The HttpContext instance.</param>
-        /// <param name="request">The service stack request instance.</param>
-        public static void SetServiceStackRequest(this HttpContext httpContext, IRequest request)
+        /// <param name="request">The incoming HTTP request.</param>
+        /// <returns><c>true</c> if the request is coming from LAN, <c>false</c> otherwise.</returns>
+        public static bool IsLocal(this HttpRequest request)
         {
-            httpContext.Items[ServiceStackRequest] = request;
+            return (request.HttpContext.Connection.LocalIpAddress == null
+                    && request.HttpContext.Connection.RemoteIpAddress == null)
+                   || request.HttpContext.Connection.LocalIpAddress.Equals(request.HttpContext.Connection.RemoteIpAddress);
         }
 
         /// <summary>
-        /// Get the ServiceStack request.
+        /// Extracts the remote IP address of the caller of the HTTP request.
         /// </summary>
-        /// <param name="httpContext">The HttpContext instance.</param>
-        /// <returns>The service stack request instance.</returns>
-        public static IRequest GetServiceStackRequest(this HttpContext httpContext)
+        /// <param name="request">The HTTP request.</param>
+        /// <returns>The remote caller IP address.</returns>
+        public static string RemoteIp(this HttpRequest request)
         {
-            return (IRequest)httpContext.Items[ServiceStackRequest];
+            if (string.IsNullOrEmpty(request.HttpContext.Items["RemoteIp"].ToString()))
+            {
+                return request.HttpContext.Items["RemoteIp"].ToString();
+            }
+
+            IPAddress ip;
+
+            // "Real" remote ip might be in X-Forwarded-For of X-Real-Ip
+            // (if the server is behind a reverse proxy for example)
+            if (!IPAddress.TryParse(request.Headers[CustomHeaderNames.XForwardedFor].ToString(), out ip))
+            {
+                if (!IPAddress.TryParse(request.Headers[CustomHeaderNames.XRealIP].ToString(), out ip))
+                {
+                    ip = request.HttpContext.Connection.RemoteIpAddress;
+
+                    // Default to the loopback address if no RemoteIpAddress is specified (i.e. during integration tests)
+                    ip ??= IPAddress.Loopback;
+                }
+            }
+
+            if (ip.IsIPv4MappedToIPv6)
+            {
+                ip = ip.MapToIPv4();
+            }
+
+            var normalizedIp = ip.ToString();
+
+            request.HttpContext.Items["RemoteIp"] = normalizedIp;
+            return normalizedIp;
         }
     }
 }

+ 0 - 30
MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs

@@ -4,7 +4,6 @@ using System;
 using System.Collections.Generic;
 using System.Linq;
 using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Services;
 
 namespace MediaBrowser.Controller.MediaEncoding
 {
@@ -63,26 +62,20 @@ namespace MediaBrowser.Controller.MediaEncoding
         /// Gets or sets the id.
         /// </summary>
         /// <value>The id.</value>
-        [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
         public Guid Id { get; set; }
 
-        [ApiMember(Name = "MediaSourceId", Description = "The media version id, if playing an alternate version", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
         public string MediaSourceId { get; set; }
 
-        [ApiMember(Name = "DeviceId", Description = "The device id of the client requesting. Used to stop encoding processes when needed.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         public string DeviceId { get; set; }
 
-        [ApiMember(Name = "Container", Description = "Container", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
         public string Container { get; set; }
 
         /// <summary>
         /// Gets or sets the audio codec.
         /// </summary>
         /// <value>The audio codec.</value>
-        [ApiMember(Name = "AudioCodec", Description = "Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         public string AudioCodec { get; set; }
 
-        [ApiMember(Name = "EnableAutoStreamCopy", Description = "Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
         public bool EnableAutoStreamCopy { get; set; }
 
         public bool AllowVideoStreamCopy { get; set; }
@@ -95,7 +88,6 @@ namespace MediaBrowser.Controller.MediaEncoding
         /// Gets or sets the audio sample rate.
         /// </summary>
         /// <value>The audio sample rate.</value>
-        [ApiMember(Name = "AudioSampleRate", Description = "Optional. Specify a specific audio sample rate, e.g. 44100", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
         public int? AudioSampleRate { get; set; }
 
         public int? MaxAudioBitDepth { get; set; }
@@ -104,105 +96,86 @@ namespace MediaBrowser.Controller.MediaEncoding
         /// Gets or sets the audio bit rate.
         /// </summary>
         /// <value>The audio bit rate.</value>
-        [ApiMember(Name = "AudioBitRate", Description = "Optional. Specify an audio bitrate to encode to, e.g. 128000. If omitted this will be left to encoder defaults.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
         public int? AudioBitRate { get; set; }
 
         /// <summary>
         /// Gets or sets the audio channels.
         /// </summary>
         /// <value>The audio channels.</value>
-        [ApiMember(Name = "AudioChannels", Description = "Optional. Specify a specific number of audio channels to encode to, e.g. 2", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
         public int? AudioChannels { get; set; }
 
-        [ApiMember(Name = "MaxAudioChannels", Description = "Optional. Specify a maximum number of audio channels to encode to, e.g. 2", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
         public int? MaxAudioChannels { get; set; }
 
-        [ApiMember(Name = "Static", Description = "Optional. If true, the original file will be streamed statically without any encoding. Use either no url extension or the original file extension. true/false", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
         public bool Static { get; set; }
 
         /// <summary>
         /// Gets or sets the profile.
         /// </summary>
         /// <value>The profile.</value>
-        [ApiMember(Name = "Profile", Description = "Optional. Specify a specific an encoder profile (varies by encoder), e.g. main, baseline, high.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         public string Profile { get; set; }
 
         /// <summary>
         /// Gets or sets the level.
         /// </summary>
         /// <value>The level.</value>
-        [ApiMember(Name = "Level", Description = "Optional. Specify a level for the encoder profile (varies by encoder), e.g. 3, 3.1.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         public string Level { get; set; }
 
         /// <summary>
         /// Gets or sets the framerate.
         /// </summary>
         /// <value>The framerate.</value>
-        [ApiMember(Name = "Framerate", Description = "Optional. A specific video framerate to encode to, e.g. 23.976. Generally this should be omitted unless the device has specific requirements.", IsRequired = false, DataType = "double", ParameterType = "query", Verb = "GET")]
         public float? Framerate { get; set; }
 
-        [ApiMember(Name = "MaxFramerate", Description = "Optional. A specific maximum video framerate to encode to, e.g. 23.976. Generally this should be omitted unless the device has specific requirements.", IsRequired = false, DataType = "double", ParameterType = "query", Verb = "GET")]
         public float? MaxFramerate { get; set; }
 
-        [ApiMember(Name = "CopyTimestamps", Description = "Whether or not to copy timestamps when transcoding with an offset. Defaults to false.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
         public bool CopyTimestamps { get; set; }
 
         /// <summary>
         /// Gets or sets the start time ticks.
         /// </summary>
         /// <value>The start time ticks.</value>
-        [ApiMember(Name = "StartTimeTicks", Description = "Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
         public long? StartTimeTicks { get; set; }
 
         /// <summary>
         /// Gets or sets the width.
         /// </summary>
         /// <value>The width.</value>
-        [ApiMember(Name = "Width", Description = "Optional. The fixed horizontal resolution of the encoded video.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
         public int? Width { get; set; }
 
         /// <summary>
         /// Gets or sets the height.
         /// </summary>
         /// <value>The height.</value>
-        [ApiMember(Name = "Height", Description = "Optional. The fixed vertical resolution of the encoded video.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
         public int? Height { get; set; }
 
         /// <summary>
         /// Gets or sets the width of the max.
         /// </summary>
         /// <value>The width of the max.</value>
-        [ApiMember(Name = "MaxWidth", Description = "Optional. The maximum horizontal resolution of the encoded video.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
         public int? MaxWidth { get; set; }
 
         /// <summary>
         /// Gets or sets the height of the max.
         /// </summary>
         /// <value>The height of the max.</value>
-        [ApiMember(Name = "MaxHeight", Description = "Optional. The maximum vertical resolution of the encoded video.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
         public int? MaxHeight { get; set; }
 
         /// <summary>
         /// Gets or sets the video bit rate.
         /// </summary>
         /// <value>The video bit rate.</value>
-        [ApiMember(Name = "VideoBitRate", Description = "Optional. Specify a video bitrate to encode to, e.g. 500000. If omitted this will be left to encoder defaults.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
         public int? VideoBitRate { get; set; }
 
         /// <summary>
         /// Gets or sets the index of the subtitle stream.
         /// </summary>
         /// <value>The index of the subtitle stream.</value>
-        [ApiMember(Name = "SubtitleStreamIndex", Description = "Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
         public int? SubtitleStreamIndex { get; set; }
 
-        [ApiMember(Name = "SubtitleMethod", Description = "Optional. Specify the subtitle delivery method.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         public SubtitleDeliveryMethod SubtitleMethod { get; set; }
 
-        [ApiMember(Name = "MaxRefFrames", Description = "Optional.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
         public int? MaxRefFrames { get; set; }
 
-        [ApiMember(Name = "MaxVideoBitDepth", Description = "Optional.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
         public int? MaxVideoBitDepth { get; set; }
 
         public bool RequireAvc { get; set; }
@@ -223,7 +196,6 @@ namespace MediaBrowser.Controller.MediaEncoding
         /// Gets or sets the video codec.
         /// </summary>
         /// <value>The video codec.</value>
-        [ApiMember(Name = "VideoCodec", Description = "Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         public string VideoCodec { get; set; }
 
         public string SubtitleCodec { get; set; }
@@ -234,14 +206,12 @@ namespace MediaBrowser.Controller.MediaEncoding
         /// Gets or sets the index of the audio stream.
         /// </summary>
         /// <value>The index of the audio stream.</value>
-        [ApiMember(Name = "AudioStreamIndex", Description = "Optional. The index of the audio stream to use. If omitted the first audio stream will be used.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
         public int? AudioStreamIndex { get; set; }
 
         /// <summary>
         /// Gets or sets the index of the video stream.
         /// </summary>
         /// <value>The index of the video stream.</value>
-        [ApiMember(Name = "VideoStreamIndex", Description = "Optional. The index of the video stream to use. If omitted the first video stream will be used.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
         public int? VideoStreamIndex { get; set; }
 
         public EncodingContext Context { get; set; }

+ 0 - 76
MediaBrowser.Controller/Net/AuthenticatedAttribute.cs

@@ -1,76 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using MediaBrowser.Model.Services;
-using Microsoft.AspNetCore.Http;
-
-namespace MediaBrowser.Controller.Net
-{
-    public class AuthenticatedAttribute : Attribute, IHasRequestFilter, IAuthenticationAttributes
-    {
-        public static IAuthService AuthService { get; set; }
-
-        /// <summary>
-        /// Gets or sets the roles.
-        /// </summary>
-        /// <value>The roles.</value>
-        public string Roles { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether [escape parental control].
-        /// </summary>
-        /// <value><c>true</c> if [escape parental control]; otherwise, <c>false</c>.</value>
-        public bool EscapeParentalControl { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether [allow before startup wizard].
-        /// </summary>
-        /// <value><c>true</c> if [allow before startup wizard]; otherwise, <c>false</c>.</value>
-        public bool AllowBeforeStartupWizard { get; set; }
-
-        public bool AllowLocal { get; set; }
-
-        /// <summary>
-        /// The request filter is executed before the service.
-        /// </summary>
-        /// <param name="request">The http request wrapper.</param>
-        /// <param name="response">The http response wrapper.</param>
-        /// <param name="requestDto">The request DTO.</param>
-        public void RequestFilter(IRequest request, HttpResponse response, object requestDto)
-        {
-            AuthService.Authenticate(request, this);
-        }
-
-        /// <summary>
-        /// Order in which Request Filters are executed.
-        /// &lt;0 Executed before global request filters
-        /// &gt;0 Executed after global request filters
-        /// </summary>
-        /// <value>The priority.</value>
-        public int Priority => 0;
-
-        public string[] GetRoles()
-        {
-            return (Roles ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
-        }
-
-        public bool IgnoreLegacyAuth { get; set; }
-
-        public bool AllowLocalOnly { get; set; }
-    }
-
-    public interface IAuthenticationAttributes
-    {
-        bool EscapeParentalControl { get; }
-
-        bool AllowBeforeStartupWizard { get; }
-
-        bool AllowLocal { get; }
-
-        bool AllowLocalOnly { get; }
-
-        string[] GetRoles();
-
-        bool IgnoreLegacyAuth { get; }
-    }
-}

+ 0 - 17
MediaBrowser.Controller/Net/IAuthService.cs

@@ -1,7 +1,5 @@
 #nullable enable
 
-using Jellyfin.Data.Entities;
-using MediaBrowser.Model.Services;
 using Microsoft.AspNetCore.Http;
 
 namespace MediaBrowser.Controller.Net
@@ -11,21 +9,6 @@ namespace MediaBrowser.Controller.Net
     /// </summary>
     public interface IAuthService
     {
-        /// <summary>
-        /// Authenticate and authorize request.
-        /// </summary>
-        /// <param name="request">Request.</param>
-        /// <param name="authAttribtutes">Authorization attributes.</param>
-        void Authenticate(IRequest request, IAuthenticationAttributes authAttribtutes);
-
-        /// <summary>
-        /// Authenticate and authorize request.
-        /// </summary>
-        /// <param name="request">Request.</param>
-        /// <param name="authAttribtutes">Authorization attributes.</param>
-        /// <returns>Authenticated user.</returns>
-        User? Authenticate(HttpRequest request, IAuthenticationAttributes authAttribtutes);
-
         /// <summary>
         /// Authenticate request.
         /// </summary>

+ 1 - 2
MediaBrowser.Controller/Net/IAuthorizationContext.cs

@@ -1,4 +1,3 @@
-using MediaBrowser.Model.Services;
 using Microsoft.AspNetCore.Http;
 
 namespace MediaBrowser.Controller.Net
@@ -20,7 +19,7 @@ namespace MediaBrowser.Controller.Net
         /// </summary>
         /// <param name="requestContext">The request context.</param>
         /// <returns>AuthorizationInfo.</returns>
-        AuthorizationInfo GetAuthorizationInfo(IRequest requestContext);
+        AuthorizationInfo GetAuthorizationInfo(HttpContext requestContext);
 
         /// <summary>
         /// Gets the authorization information.

+ 0 - 17
MediaBrowser.Controller/Net/IHasResultFactory.cs

@@ -1,17 +0,0 @@
-using MediaBrowser.Model.Services;
-
-namespace MediaBrowser.Controller.Net
-{
-    /// <summary>
-    /// Interface IHasResultFactory
-    /// Services that require a ResultFactory should implement this
-    /// </summary>
-    public interface IHasResultFactory : IRequiresRequest
-    {
-        /// <summary>
-        /// Gets or sets the result factory.
-        /// </summary>
-        /// <value>The result factory.</value>
-        IHttpResultFactory ResultFactory { get; set; }
-    }
-}

+ 0 - 82
MediaBrowser.Controller/Net/IHttpResultFactory.cs

@@ -1,82 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Services;
-
-namespace MediaBrowser.Controller.Net
-{
-    /// <summary>
-    /// Interface IHttpResultFactory.
-    /// </summary>
-    public interface IHttpResultFactory
-    {
-        /// <summary>
-        /// Gets the result.
-        /// </summary>
-        /// <param name="content">The content.</param>
-        /// <param name="contentType">Type of the content.</param>
-        /// <param name="responseHeaders">The response headers.</param>
-        /// <returns>System.Object.</returns>
-        object GetResult(string content, string contentType, IDictionary<string, string> responseHeaders = null);
-
-        object GetResult(IRequest requestContext, byte[] content, string contentType, IDictionary<string, string> responseHeaders = null);
-
-        object GetResult(IRequest requestContext, Stream content, string contentType, IDictionary<string, string> responseHeaders = null);
-
-        object GetResult(IRequest requestContext, string content, string contentType, IDictionary<string, string> responseHeaders = null);
-
-        object GetRedirectResult(string url);
-
-        object GetResult<T>(IRequest requestContext, T result, IDictionary<string, string> responseHeaders = null)
-            where T : class;
-
-        /// <summary>
-        /// Gets the static result.
-        /// </summary>
-        /// <param name="requestContext">The request context.</param>
-        /// <param name="cacheKey">The cache key.</param>
-        /// <param name="lastDateModified">The last date modified.</param>
-        /// <param name="cacheDuration">Duration of the cache.</param>
-        /// <param name="contentType">Type of the content.</param>
-        /// <param name="factoryFn">The factory fn.</param>
-        /// <param name="responseHeaders">The response headers.</param>
-        /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
-        /// <returns>System.Object.</returns>
-        Task<object> GetStaticResult(IRequest requestContext,
-            Guid cacheKey,
-            DateTime? lastDateModified,
-            TimeSpan? cacheDuration,
-            string contentType, Func<Task<Stream>> factoryFn,
-            IDictionary<string, string> responseHeaders = null,
-            bool isHeadRequest = false);
-
-        /// <summary>
-        /// Gets the static result.
-        /// </summary>
-        /// <param name="requestContext">The request context.</param>
-        /// <param name="options">The options.</param>
-        /// <returns>System.Object.</returns>
-        Task<object> GetStaticResult(IRequest requestContext, StaticResultOptions options);
-
-        /// <summary>
-        /// Gets the static file result.
-        /// </summary>
-        /// <param name="requestContext">The request context.</param>
-        /// <param name="path">The path.</param>
-        /// <param name="fileShare">The file share.</param>
-        /// <returns>System.Object.</returns>
-        Task<object> GetStaticFileResult(IRequest requestContext, string path, FileShare fileShare = FileShare.Read);
-
-        /// <summary>
-        /// Gets the static file result.
-        /// </summary>
-        /// <param name="requestContext">The request context.</param>
-        /// <param name="options">The options.</param>
-        /// <returns>System.Object.</returns>
-        Task<object> GetStaticFileResult(IRequest requestContext,
-            StaticFileResultOptions options);
-    }
-}

+ 4 - 5
MediaBrowser.Controller/Net/IHttpServer.cs

@@ -2,7 +2,6 @@ using System;
 using System.Collections.Generic;
 using System.Threading.Tasks;
 using Jellyfin.Data.Events;
-using MediaBrowser.Model.Services;
 using Microsoft.AspNetCore.Http;
 
 namespace MediaBrowser.Controller.Net
@@ -26,7 +25,7 @@ namespace MediaBrowser.Controller.Net
         /// <summary>
         /// Inits this instance.
         /// </summary>
-        void Init(IEnumerable<Type> serviceTypes, IEnumerable<IWebSocketListener> listener, IEnumerable<string> urlPrefixes);
+        void Init(IEnumerable<IWebSocketListener> listener, IEnumerable<string> urlPrefixes);
 
         /// <summary>
         /// If set, all requests will respond with this message.
@@ -43,8 +42,8 @@ namespace MediaBrowser.Controller.Net
         /// <summary>
         /// Get the default CORS headers.
         /// </summary>
-        /// <param name="req"></param>
-        /// <returns></returns>
-        IDictionary<string, string> GetDefaultCorsHeaders(IRequest req);
+        /// <param name="req">The HTTP context of the current request.</param>
+        /// <returns>The default CORS headers for the context.</returns>
+        IDictionary<string, string> GetDefaultCorsHeaders(HttpContext httpContext);
     }
 }

+ 3 - 3
MediaBrowser.Controller/Net/ISessionContext.cs

@@ -2,7 +2,7 @@
 
 using Jellyfin.Data.Entities;
 using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Services;
+using Microsoft.AspNetCore.Http;
 
 namespace MediaBrowser.Controller.Net
 {
@@ -12,8 +12,8 @@ namespace MediaBrowser.Controller.Net
 
         User GetUser(object requestContext);
 
-        SessionInfo GetSession(IRequest requestContext);
+        SessionInfo GetSession(HttpContext requestContext);
 
-        User GetUser(IRequest requestContext);
+        User GetUser(HttpContext requestContext);
     }
 }

+ 0 - 44
MediaBrowser.Controller/Net/StaticResultOptions.cs

@@ -1,44 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Net
-{
-    public class StaticResultOptions
-    {
-        public string ContentType { get; set; }
-
-        public TimeSpan? CacheDuration { get; set; }
-
-        public DateTime? DateLastModified { get; set; }
-
-        public Func<Task<Stream>> ContentFactory { get; set; }
-
-        public bool IsHeadRequest { get; set; }
-
-        public IDictionary<string, string> ResponseHeaders { get; set; }
-
-        public Action OnComplete { get; set; }
-
-        public Action OnError { get; set; }
-
-        public string Path { get; set; }
-
-        public long? ContentLength { get; set; }
-
-        public FileShare FileShare { get; set; }
-
-        public StaticResultOptions()
-        {
-            ResponseHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-            FileShare = FileShare.Read;
-        }
-    }
-
-    public class StaticFileResultOptions : StaticResultOptions
-    {
-    }
-}

+ 0 - 65
MediaBrowser.Model/Services/ApiMemberAttribute.cs

@@ -1,65 +0,0 @@
-#nullable disable
-using System;
-
-namespace MediaBrowser.Model.Services
-{
-    /// <summary>
-    /// Identifies a single API endpoint.
-    /// </summary>
-    [AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
-    public class ApiMemberAttribute : Attribute
-    {
-        /// <summary>
-        /// Gets or sets verb to which applies attribute. By default applies to all verbs.
-        /// </summary>
-        public string Verb { get; set; }
-
-        /// <summary>
-        /// Gets or sets parameter type: It can be only one of the following: path, query, body, form, or header.
-        /// </summary>
-        public string ParameterType { get; set; }
-
-        /// <summary>
-        /// Gets or sets unique name for the parameter. Each name must be unique, even if they are associated with different paramType values.
-        /// </summary>
-        /// <remarks>
-        /// <para>
-        /// Other notes on the name field:
-        /// If paramType is body, the name is used only for UI and codegeneration.
-        /// If paramType is path, the name field must correspond to the associated path segment from the path field in the api object.
-        /// If paramType is query, the name field corresponds to the query param name.
-        /// </para>
-        /// </remarks>
-        public string Name { get; set; }
-
-        /// <summary>
-        /// Gets or sets the human-readable description for the parameter.
-        /// </summary>
-        public string Description { get; set; }
-
-        /// <summary>
-        /// For path, query, and header paramTypes, this field must be a primitive. For body, this can be a complex or container datatype.
-        /// </summary>
-        public string DataType { get; set; }
-
-        /// <summary>
-        /// For path, this is always true. Otherwise, this field tells the client whether or not the field must be supplied.
-        /// </summary>
-        public bool IsRequired { get; set; }
-
-        /// <summary>
-        /// For query params, this specifies that a comma-separated list of values can be passed to the API. For path and body types, this field cannot be true.
-        /// </summary>
-        public bool AllowMultiple { get; set; }
-
-        /// <summary>
-        /// Gets or sets route to which applies attribute, matches using StartsWith. By default applies to all routes.
-        /// </summary>
-        public string Route { get; set; }
-
-        /// <summary>
-        /// Whether to exclude this property from being included in the ModelSchema.
-        /// </summary>
-        public bool ExcludeInSchema { get; set; }
-    }
-}

+ 0 - 13
MediaBrowser.Model/Services/IAsyncStreamWriter.cs

@@ -1,13 +0,0 @@
-#pragma warning disable CS1591
-
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Model.Services
-{
-    public interface IAsyncStreamWriter
-    {
-        Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken);
-    }
-}

+ 0 - 11
MediaBrowser.Model/Services/IHasHeaders.cs

@@ -1,11 +0,0 @@
-#pragma warning disable CS1591
-
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Services
-{
-    public interface IHasHeaders
-    {
-        IDictionary<string, string> Headers { get; }
-    }
-}

+ 0 - 24
MediaBrowser.Model/Services/IHasRequestFilter.cs

@@ -1,24 +0,0 @@
-#pragma warning disable CS1591
-
-using Microsoft.AspNetCore.Http;
-
-namespace MediaBrowser.Model.Services
-{
-    public interface IHasRequestFilter
-    {
-        /// <summary>
-        /// Gets the order in which Request Filters are executed.
-        /// &lt;0 Executed before global request filters.
-        /// &gt;0 Executed after global request filters.
-        /// </summary>
-        int Priority { get; }
-
-        /// <summary>
-        /// The request filter is executed before the service.
-        /// </summary>
-        /// <param name="req">The http request wrapper.</param>
-        /// <param name="res">The http response wrapper.</param>
-        /// <param name="requestDto">The request DTO.</param>
-        void RequestFilter(IRequest req, HttpResponse res, object requestDto);
-    }
-}

+ 0 - 17
MediaBrowser.Model/Services/IHttpRequest.cs

@@ -1,17 +0,0 @@
-#pragma warning disable CS1591
-
-namespace MediaBrowser.Model.Services
-{
-    public interface IHttpRequest : IRequest
-    {
-        /// <summary>
-        /// Gets the HTTP Verb.
-        /// </summary>
-        string HttpMethod { get; }
-
-        /// <summary>
-        /// Gets the value of the Accept HTTP Request Header.
-        /// </summary>
-        string Accept { get; }
-    }
-}

+ 0 - 35
MediaBrowser.Model/Services/IHttpResult.cs

@@ -1,35 +0,0 @@
-#nullable disable
-#pragma warning disable CS1591
-
-using System.Net;
-
-namespace MediaBrowser.Model.Services
-{
-    public interface IHttpResult : IHasHeaders
-    {
-        /// <summary>
-        /// The HTTP Response Status.
-        /// </summary>
-        int Status { get; set; }
-
-        /// <summary>
-        /// The HTTP Response Status Code.
-        /// </summary>
-        HttpStatusCode StatusCode { get; set; }
-
-        /// <summary>
-        /// The HTTP Response ContentType.
-        /// </summary>
-        string ContentType { get; set; }
-
-        /// <summary>
-        /// Response DTO.
-        /// </summary>
-        object Response { get; set; }
-
-        /// <summary>
-        /// Holds the request call context.
-        /// </summary>
-        IRequest RequestContext { get; set; }
-    }
-}

+ 0 - 93
MediaBrowser.Model/Services/IRequest.cs

@@ -1,93 +0,0 @@
-#nullable disable
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using Microsoft.AspNetCore.Http;
-
-namespace MediaBrowser.Model.Services
-{
-    public interface IRequest
-    {
-        HttpResponse Response { get; }
-
-        /// <summary>
-        /// The name of the service being called (e.g. Request DTO Name)
-        /// </summary>
-        string OperationName { get; set; }
-
-        /// <summary>
-        /// The Verb / HttpMethod or Action for this request
-        /// </summary>
-        string Verb { get; }
-
-        /// <summary>
-        /// The request ContentType.
-        /// </summary>
-        string ContentType { get; }
-
-        bool IsLocal { get; }
-
-        string UserAgent { get; }
-
-        /// <summary>
-        /// The expected Response ContentType for this request.
-        /// </summary>
-        string ResponseContentType { get; set; }
-
-        /// <summary>
-        /// Attach any data to this request that all filters and services can access.
-        /// </summary>
-        Dictionary<string, object> Items { get; }
-
-        IHeaderDictionary Headers { get; }
-
-        IQueryCollection QueryString { get; }
-
-        string RawUrl { get; }
-
-        string AbsoluteUri { get; }
-
-        /// <summary>
-        /// The Remote Ip as reported by X-Forwarded-For, X-Real-IP or Request.UserHostAddress
-        /// </summary>
-        string RemoteIp { get; }
-
-        /// <summary>
-        /// The value of the Authorization Header used to send the Api Key, null if not available.
-        /// </summary>
-        string Authorization { get; }
-
-        string[] AcceptTypes { get; }
-
-        string PathInfo { get; }
-
-        Stream InputStream { get; }
-
-        long ContentLength { get; }
-
-        /// <summary>
-        /// The value of the Referrer, null if not available.
-        /// </summary>
-        Uri UrlReferrer { get; }
-    }
-
-    public interface IHttpFile
-    {
-        string Name { get; }
-
-        string FileName { get; }
-
-        long ContentLength { get; }
-
-        string ContentType { get; }
-
-        Stream InputStream { get; }
-    }
-
-    public interface IRequiresRequest
-    {
-        IRequest Request { get; set; }
-    }
-}

+ 0 - 14
MediaBrowser.Model/Services/IRequiresRequestStream.cs

@@ -1,14 +0,0 @@
-#pragma warning disable CS1591
-
-using System.IO;
-
-namespace MediaBrowser.Model.Services
-{
-    public interface IRequiresRequestStream
-    {
-        /// <summary>
-        /// The raw Http Request Input Stream.
-        /// </summary>
-        Stream RequestStream { get; set; }
-    }
-}

+ 0 - 15
MediaBrowser.Model/Services/IService.cs

@@ -1,15 +0,0 @@
-#pragma warning disable CS1591
-
-namespace MediaBrowser.Model.Services
-{
-    // marker interface
-    public interface IService
-    {
-    }
-
-    public interface IReturn { }
-
-    public interface IReturn<T> : IReturn { }
-
-    public interface IReturnVoid : IReturn { }
-}

+ 0 - 11
MediaBrowser.Model/Services/IStreamWriter.cs

@@ -1,11 +0,0 @@
-#pragma warning disable CS1591
-
-using System.IO;
-
-namespace MediaBrowser.Model.Services
-{
-    public interface IStreamWriter
-    {
-        void WriteTo(Stream responseStream);
-    }
-}

+ 0 - 147
MediaBrowser.Model/Services/QueryParamCollection.cs

@@ -1,147 +0,0 @@
-#nullable disable
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using MediaBrowser.Model.Dto;
-
-namespace MediaBrowser.Model.Services
-{
-    // Remove this garbage class, it's just a bastard copy of NameValueCollection
-    public class QueryParamCollection : List<NameValuePair>
-    {
-        public QueryParamCollection()
-        {
-        }
-
-        private static StringComparison GetStringComparison()
-        {
-            return StringComparison.OrdinalIgnoreCase;
-        }
-
-        /// <summary>
-        /// Adds a new query parameter.
-        /// </summary>
-        public void Add(string key, string value)
-        {
-            Add(new NameValuePair(key, value));
-        }
-
-        private void Set(string key, string value)
-        {
-            if (string.IsNullOrEmpty(value))
-            {
-                var parameters = GetItems(key);
-
-                foreach (var p in parameters)
-                {
-                    Remove(p);
-                }
-
-                return;
-            }
-
-            foreach (var pair in this)
-            {
-                var stringComparison = GetStringComparison();
-
-                if (string.Equals(key, pair.Name, stringComparison))
-                {
-                    pair.Value = value;
-                    return;
-                }
-            }
-
-            Add(key, value);
-        }
-
-        private string Get(string name)
-        {
-            var stringComparison = GetStringComparison();
-
-            foreach (var pair in this)
-            {
-                if (string.Equals(pair.Name, name, stringComparison))
-                {
-                    return pair.Value;
-                }
-            }
-
-            return null;
-        }
-
-        private List<NameValuePair> GetItems(string name)
-        {
-            var stringComparison = GetStringComparison();
-
-            var list = new List<NameValuePair>();
-
-            foreach (var pair in this)
-            {
-                if (string.Equals(pair.Name, name, stringComparison))
-                {
-                    list.Add(pair);
-                }
-            }
-
-            return list;
-        }
-
-        public virtual List<string> GetValues(string name)
-        {
-            var stringComparison = GetStringComparison();
-
-            var list = new List<string>();
-
-            foreach (var pair in this)
-            {
-                if (string.Equals(pair.Name, name, stringComparison))
-                {
-                    list.Add(pair.Value);
-                }
-            }
-
-            return list;
-        }
-
-        public IEnumerable<string> Keys
-        {
-            get
-            {
-                var keys = new string[this.Count];
-
-                for (var i = 0; i < keys.Length; i++)
-                {
-                    keys[i] = this[i].Name;
-                }
-
-                return keys;
-            }
-        }
-
-        /// <summary>
-        /// Gets or sets a query parameter value by name. A query may contain multiple values of the same name
-        /// (i.e. "x=1&amp;x=2"), in which case the value is an array, which works for both getting and setting.
-        /// </summary>
-        /// <param name="name">The query parameter name.</param>
-        /// <returns>The query parameter value or array of values.</returns>
-        public string this[string name]
-        {
-            get => Get(name);
-            set => Set(name, value);
-        }
-
-        private string GetQueryStringValue(NameValuePair pair)
-        {
-            return pair.Name + "=" + pair.Value;
-        }
-
-        public override string ToString()
-        {
-            var vals = this.Select(GetQueryStringValue).ToArray();
-
-            return string.Join("&", vals);
-        }
-    }
-}

+ 0 - 163
MediaBrowser.Model/Services/RouteAttribute.cs

@@ -1,163 +0,0 @@
-#nullable disable
-#pragma warning disable CS1591
-
-using System;
-
-namespace MediaBrowser.Model.Services
-{
-    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
-    public class RouteAttribute : Attribute
-    {
-        /// <summary>
-        ///     <para>Initializes an instance of the <see cref="RouteAttribute"/> class.</para>
-        /// </summary>
-        /// <param name="path">
-        ///     <para>The path template to map to the request.  See
-        ///        <see cref="Path">RouteAttribute.Path</see>
-        ///        for details on the correct format.</para>
-        /// </param>
-        public RouteAttribute(string path)
-            : this(path, null)
-        {
-        }
-
-        /// <summary>
-        ///     <para>Initializes an instance of the <see cref="RouteAttribute"/> class.</para>
-        /// </summary>
-        /// <param name="path">
-        ///     <para>The path template to map to the request.  See
-        ///         <see cref="Path">RouteAttribute.Path</see>
-        ///         for details on the correct format.</para>
-        /// </param>
-        /// <param name="verbs">A comma-delimited list of HTTP verbs supported by the
-        ///     service.  If unspecified, all verbs are assumed to be supported.</param>
-        public RouteAttribute(string path, string verbs)
-        {
-            Path = path;
-            Verbs = verbs;
-        }
-
-        /// <summary>
-        ///     Gets or sets the path template to be mapped to the request.
-        /// </summary>
-        /// <value>
-        ///     A <see cref="String"/> value providing the path mapped to
-        ///     the request.  Never <see langword="null"/>.
-        /// </value>
-        /// <remarks>
-        ///     <para>Some examples of valid paths are:</para>
-        ///
-        ///     <list>
-        ///         <item>"/Inventory"</item>
-        ///         <item>"/Inventory/{Category}/{ItemId}"</item>
-        ///         <item>"/Inventory/{ItemPath*}"</item>
-        ///     </list>
-        ///
-        ///     <para>Variables are specified within "{}"
-        ///     brackets.  Each variable in the path is mapped to the same-named property
-        ///     on the request DTO.  At runtime, ServiceStack will parse the
-        ///     request URL, extract the variable values, instantiate the request DTO,
-        ///     and assign the variable values into the corresponding request properties,
-        ///     prior to passing the request DTO to the service object for processing.</para>
-        ///
-        ///     <para>It is not necessary to specify all request properties as
-        ///     variables in the path.  For unspecified properties, callers may provide
-        ///     values in the query string.  For example: the URL
-        ///     "http://services/Inventory?Category=Books&amp;ItemId=12345" causes the same
-        ///     request DTO to be processed as "http://services/Inventory/Books/12345",
-        ///     provided that the paths "/Inventory" (which supports the first URL) and
-        ///     "/Inventory/{Category}/{ItemId}" (which supports the second URL)
-        ///     are both mapped to the request DTO.</para>
-        ///
-        ///     <para>Please note that while it is possible to specify property values
-        ///     in the query string, it is generally considered to be less RESTful and
-        ///     less desirable than to specify them as variables in the path.  Using the
-        ///     query string to specify property values may also interfere with HTTP
-        ///     caching.</para>
-        ///
-        ///     <para>The final variable in the path may contain a "*" suffix
-        ///     to grab all remaining segments in the path portion of the request URL and assign
-        ///     them to a single property on the request DTO.
-        ///     For example, if the path "/Inventory/{ItemPath*}" is mapped to the request DTO,
-        ///     then the request URL "http://services/Inventory/Books/12345" will result
-        ///     in a request DTO whose ItemPath property contains "Books/12345".
-        ///     You may only specify one such variable in the path, and it must be positioned at
-        ///     the end of the path.</para>
-        /// </remarks>
-        public string Path { get; set; }
-
-        /// <summary>
-        ///    Gets or sets short summary of what the route does.
-        /// </summary>
-        public string Summary { get; set; }
-
-        public string Description { get; set; }
-
-        public bool IsHidden { get; set; }
-
-        /// <summary>
-        ///    Gets or sets longer text to explain the behaviour of the route.
-        /// </summary>
-        public string Notes { get; set; }
-
-        /// <summary>
-        ///     Gets or sets a comma-delimited list of HTTP verbs supported by the service, such as
-        ///     "GET,PUT,POST,DELETE".
-        /// </summary>
-        /// <value>
-        ///     A <see cref="String"/> providing a comma-delimited list of HTTP verbs supported
-        ///     by the service, <see langword="null"/> or empty if all verbs are supported.
-        /// </value>
-        public string Verbs { get; set; }
-
-        /// <summary>
-        /// Used to rank the precedences of route definitions in reverse routing.
-        /// i.e. Priorities below 0 are auto-generated have less precedence.
-        /// </summary>
-        public int Priority { get; set; }
-
-        protected bool Equals(RouteAttribute other)
-        {
-            return base.Equals(other)
-                && string.Equals(Path, other.Path)
-                && string.Equals(Summary, other.Summary)
-                && string.Equals(Notes, other.Notes)
-                && string.Equals(Verbs, other.Verbs)
-                && Priority == other.Priority;
-        }
-
-        public override bool Equals(object obj)
-        {
-            if (ReferenceEquals(null, obj))
-            {
-                return false;
-            }
-
-            if (ReferenceEquals(this, obj))
-            {
-                return true;
-            }
-
-            if (obj.GetType() != this.GetType())
-            {
-                return false;
-            }
-
-            return Equals((RouteAttribute)obj);
-        }
-
-        public override int GetHashCode()
-        {
-            unchecked
-            {
-                var hashCode = base.GetHashCode();
-                hashCode = (hashCode * 397) ^ (Path != null ? Path.GetHashCode() : 0);
-                hashCode = (hashCode * 397) ^ (Summary != null ? Summary.GetHashCode() : 0);
-                hashCode = (hashCode * 397) ^ (Notes != null ? Notes.GetHashCode() : 0);
-                hashCode = (hashCode * 397) ^ (Verbs != null ? Verbs.GetHashCode() : 0);
-                hashCode = (hashCode * 397) ^ Priority;
-                return hashCode;
-            }
-        }
-    }
-}

+ 0 - 1
MediaBrowser.Model/Session/PlayRequest.cs

@@ -2,7 +2,6 @@
 #pragma warning disable CS1591
 
 using System;
-using MediaBrowser.Model.Services;
 
 namespace MediaBrowser.Model.Session
 {

+ 0 - 18
tests/Jellyfin.Server.Implementations.Tests/HttpServer/ResponseFilterTests.cs

@@ -1,18 +0,0 @@
-using Emby.Server.Implementations.HttpServer;
-using Xunit;
-
-namespace Jellyfin.Server.Implementations.Tests.HttpServer
-{
-    public class ResponseFilterTests
-    {
-        [Theory]
-        [InlineData(null, null)]
-        [InlineData("", "")]
-        [InlineData("This is a clean string.", "This is a clean string.")]
-        [InlineData("This isn't \n\ra clean string.", "This isn't a clean string.")]
-        public void RemoveControlCharacters_ValidArgs_Correct(string? input, string? result)
-        {
-            Assert.Equal(result, ResponseFilter.RemoveControlCharacters(input));
-        }
-    }
-}