diff options
| author | Joshua M. Boniface <joshua@boniface.me> | 2019-08-19 14:57:48 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-08-19 14:57:48 -0400 |
| commit | d95c04787cc4486f4ea5caaef20f6ac407a3f84a (patch) | |
| tree | 7e3193614c5a132ae63034c2bb7e07956a5670e6 /Emby.Server.Implementations/HttpServer/HttpListenerHost.cs | |
| parent | 3ba709fcc32d7255a2cb2466dde8c2479130a2bc (diff) | |
| parent | d99278da1dcac4d3c60739e864597aa01f916636 (diff) | |
Merge branch 'master' into h265
Diffstat (limited to 'Emby.Server.Implementations/HttpServer/HttpListenerHost.cs')
| -rw-r--r-- | Emby.Server.Implementations/HttpServer/HttpListenerHost.cs | 247 |
1 files changed, 65 insertions, 182 deletions
diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index b3d2b9cc2..bdcf5d0b7 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -5,9 +5,9 @@ using System.IO; using System.Linq; using System.Net.Sockets; using System.Reflection; -using System.Text; using System.Threading; using System.Threading.Tasks; +using Emby.Server.Implementations.Configuration; using Emby.Server.Implementations.Net; using Emby.Server.Implementations.Services; using MediaBrowser.Common.Extensions; @@ -30,11 +30,7 @@ namespace Emby.Server.Implementations.HttpServer { public class HttpListenerHost : IHttpServer, IDisposable { - private string DefaultRedirectPath { get; set; } - public string[] UrlPrefixes { get; private set; } - - public event EventHandler<GenericEventArgs<IWebSocketConnection>> WebSocketConnected; - + private readonly ILogger _logger; private readonly IServerConfigurationManager _config; private readonly INetworkManager _networkManager; private readonly IServerApplicationHost _appHost; @@ -42,18 +38,15 @@ namespace Emby.Server.Implementations.HttpServer private readonly IXmlSerializer _xmlSerializer; private readonly IHttpListener _socketListener; private readonly Func<Type, Func<string, object>> _funcParseFn; - - public Action<IRequest, IResponse, object>[] ResponseFilters { get; set; } - + private readonly string _defaultRedirectPath; private readonly Dictionary<Type, Type> ServiceOperationsMap = new Dictionary<Type, Type>(); - public static HttpListenerHost Instance { get; protected set; } - private IWebSocketListener[] _webSocketListeners = Array.Empty<IWebSocketListener>(); private readonly List<IWebSocketConnection> _webSocketConnections = new List<IWebSocketConnection>(); + private bool _disposed = false; public HttpListenerHost( IServerApplicationHost applicationHost, - ILoggerFactory loggerFactory, + ILogger<HttpListenerHost> logger, IServerConfigurationManager config, IConfiguration configuration, INetworkManager networkManager, @@ -62,9 +55,9 @@ namespace Emby.Server.Implementations.HttpServer IHttpListener socketListener) { _appHost = applicationHost; - Logger = loggerFactory.CreateLogger("HttpServer"); + _logger = logger; _config = config; - DefaultRedirectPath = configuration["HttpListenerHost:DefaultRedirectPath"]; + _defaultRedirectPath = configuration["HttpListenerHost:DefaultRedirectPath"]; _networkManager = networkManager; _jsonSerializer = jsonSerializer; _xmlSerializer = xmlSerializer; @@ -74,12 +67,20 @@ namespace Emby.Server.Implementations.HttpServer _funcParseFn = t => s => JsvReader.GetParseFn(t)(s); Instance = this; - ResponseFilters = Array.Empty<Action<IRequest, IResponse, object>>(); + ResponseFilters = Array.Empty<Action<IRequest, HttpResponse, object>>(); } + 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; } - protected ILogger Logger { get; } + public ServiceController ServiceController { get; private set; } + + public event EventHandler<GenericEventArgs<IWebSocketConnection>> WebSocketConnected; public object CreateInstance(Type type) { @@ -91,7 +92,7 @@ namespace Emby.Server.Implementations.HttpServer /// and no more processing should be done. /// </summary> /// <returns></returns> - public void ApplyRequestFilters(IRequest req, IResponse res, object requestDto) + public void ApplyRequestFilters(IRequest req, HttpResponse res, object requestDto) { //Exec all RequestFilter attributes with Priority < 0 var attributes = GetRequestFilterAttributes(requestDto.GetType()); @@ -145,7 +146,7 @@ namespace Emby.Server.Implementations.HttpServer return; } - var connection = new WebSocketConnection(e.WebSocket, e.Endpoint, _jsonSerializer, Logger) + var connection = new WebSocketConnection(e.WebSocket, e.Endpoint, _jsonSerializer, _logger) { OnReceive = ProcessWebSocketMessageReceived, Url = e.Url, @@ -215,16 +216,16 @@ namespace Emby.Server.Implementations.HttpServer if (logExceptionStackTrace) { - Logger.LogError(ex, "Error processing request"); + _logger.LogError(ex, "Error processing request"); } else if (logExceptionMessage) { - Logger.LogError(ex.Message); + _logger.LogError(ex.Message); } var httpRes = httpReq.Response; - if (httpRes.OriginalResponse.HasStarted) + if (httpRes.HasStarted) { return; } @@ -233,11 +234,11 @@ namespace Emby.Server.Implementations.HttpServer httpRes.StatusCode = statusCode; httpRes.ContentType = "text/html"; - await Write(httpRes, NormalizeExceptionMessage(ex.Message)).ConfigureAwait(false); + await httpRes.WriteAsync(NormalizeExceptionMessage(ex.Message)).ConfigureAwait(false); } catch (Exception errorEx) { - Logger.LogError(errorEx, "Error this.ProcessRequest(context)(Exception while writing error to the response)"); + _logger.LogError(errorEx, "Error this.ProcessRequest(context)(Exception while writing error to the response)"); } } @@ -431,7 +432,7 @@ namespace Emby.Server.Implementations.HttpServer { httpRes.StatusCode = 503; httpRes.ContentType = "text/plain"; - await Write(httpRes, "Server shutting down").ConfigureAwait(false); + await httpRes.WriteAsync("Server shutting down", cancellationToken).ConfigureAwait(false); return; } @@ -439,7 +440,7 @@ namespace Emby.Server.Implementations.HttpServer { httpRes.StatusCode = 400; httpRes.ContentType = "text/plain"; - await Write(httpRes, "Invalid host").ConfigureAwait(false); + await httpRes.WriteAsync("Invalid host", cancellationToken).ConfigureAwait(false); return; } @@ -447,7 +448,7 @@ namespace Emby.Server.Implementations.HttpServer { httpRes.StatusCode = 403; httpRes.ContentType = "text/plain"; - await Write(httpRes, "Forbidden").ConfigureAwait(false); + await httpRes.WriteAsync("Forbidden", cancellationToken).ConfigureAwait(false); return; } @@ -460,101 +461,35 @@ namespace Emby.Server.Implementations.HttpServer if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase)) { httpRes.StatusCode = 200; - httpRes.AddHeader("Access-Control-Allow-Origin", "*"); - httpRes.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS"); - httpRes.AddHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization"); + httpRes.Headers.Add("Access-Control-Allow-Origin", "*"); + httpRes.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS"); + httpRes.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization"); httpRes.ContentType = "text/plain"; - await Write(httpRes, string.Empty).ConfigureAwait(false); + await httpRes.WriteAsync(string.Empty, cancellationToken).ConfigureAwait(false); return; } urlToLog = GetUrlToLog(urlString); - Logger.LogDebug("HTTP {HttpMethod} {Url} UserAgent: {UserAgent} \nHeaders: {@Headers}", urlToLog, httpReq.UserAgent ?? string.Empty, httpReq.HttpMethod, httpReq.Headers); - - if (string.Equals(localPath, "/emby/", StringComparison.OrdinalIgnoreCase) || - string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase)) - { - RedirectToUrl(httpRes, DefaultRedirectPath); - return; - } - if (string.Equals(localPath, "/emby", StringComparison.OrdinalIgnoreCase) || - string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(localPath, "/" + _config.Configuration.BaseUrl + "/", StringComparison.OrdinalIgnoreCase) + || string.Equals(localPath, "/" + _config.Configuration.BaseUrl, StringComparison.OrdinalIgnoreCase)) { - RedirectToUrl(httpRes, "emby/" + DefaultRedirectPath); - return; - } - - if (localPath.IndexOf("mediabrowser/web", StringComparison.OrdinalIgnoreCase) != -1) - { - httpRes.StatusCode = 200; - httpRes.ContentType = "text/html"; - var newUrl = urlString.Replace("mediabrowser", "emby", StringComparison.OrdinalIgnoreCase) - .Replace("/dashboard/", "/web/", StringComparison.OrdinalIgnoreCase); - - if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase)) - { - await Write(httpRes, - "<!doctype html><html><head><title>Emby</title></head><body>Please update your Emby bookmark to <a href=\"" + - newUrl + "\">" + newUrl + "</a></body></html>").ConfigureAwait(false); - return; - } - } - - if (localPath.IndexOf("dashboard/", StringComparison.OrdinalIgnoreCase) != -1 && - localPath.IndexOf("web/dashboard", StringComparison.OrdinalIgnoreCase) == -1) - { - httpRes.StatusCode = 200; - httpRes.ContentType = "text/html"; - var newUrl = urlString.Replace("mediabrowser", "emby", StringComparison.OrdinalIgnoreCase) - .Replace("/dashboard/", "/web/", StringComparison.OrdinalIgnoreCase); - - if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase)) - { - await Write(httpRes, - "<!doctype html><html><head><title>Emby</title></head><body>Please update your Emby bookmark to <a href=\"" + - newUrl + "\">" + newUrl + "</a></body></html>").ConfigureAwait(false); - return; - } - } - - if (string.Equals(localPath, "/web", StringComparison.OrdinalIgnoreCase)) - { - RedirectToUrl(httpRes, DefaultRedirectPath); - return; - } - - if (string.Equals(localPath, "/web/", StringComparison.OrdinalIgnoreCase)) - { - RedirectToUrl(httpRes, "../" + DefaultRedirectPath); + httpRes.Redirect("/" + _config.Configuration.BaseUrl + "/" + _defaultRedirectPath); return; } if (string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase)) { - RedirectToUrl(httpRes, DefaultRedirectPath); + httpRes.Redirect(_defaultRedirectPath); return; } if (string.IsNullOrEmpty(localPath)) { - RedirectToUrl(httpRes, "/" + DefaultRedirectPath); + httpRes.Redirect("/" + _defaultRedirectPath); return; } - if (!string.Equals(httpReq.QueryString["r"], "0", StringComparison.OrdinalIgnoreCase)) - { - if (localPath.EndsWith("web/dashboard.html", StringComparison.OrdinalIgnoreCase)) - { - RedirectToUrl(httpRes, "index.html#!/dashboard.html"); - } - - if (localPath.EndsWith("web/home.html", StringComparison.OrdinalIgnoreCase)) - { - RedirectToUrl(httpRes, "index.html"); - } - } - if (!string.IsNullOrEmpty(GlobalResponse)) { // We don't want the address pings in ApplicationHost to fail @@ -562,16 +497,15 @@ namespace Emby.Server.Implementations.HttpServer { httpRes.StatusCode = 503; httpRes.ContentType = "text/html"; - await Write(httpRes, GlobalResponse).ConfigureAwait(false); + await httpRes.WriteAsync(GlobalResponse, cancellationToken).ConfigureAwait(false); return; } } var handler = GetServiceHandler(httpReq); - if (handler != null) { - await handler.ProcessRequestAsync(this, httpReq, httpRes, Logger, cancellationToken).ConfigureAwait(false); + await handler.ProcessRequestAsync(this, httpReq, httpRes, _logger, cancellationToken).ConfigureAwait(false); } else { @@ -598,11 +532,7 @@ namespace Emby.Server.Implementations.HttpServer var elapsed = stopWatch.Elapsed; if (elapsed.TotalMilliseconds > 500) { - Logger.LogWarning("HTTP Response {StatusCode} to {RemoteIp}. Time (slow): {Elapsed:g}. {Url}", httpRes.StatusCode, remoteIp, elapsed, urlToLog); - } - else - { - Logger.LogDebug("HTTP Response {StatusCode} to {RemoteIp}. Time: {Elapsed:g}. {Url}", httpRes.StatusCode, remoteIp, elapsed, urlToLog); + _logger.LogWarning("HTTP Response {StatusCode} to {RemoteIp}. Time (slow): {Elapsed:g}. {Url}", httpRes.StatusCode, remoteIp, elapsed, urlToLog); } } } @@ -619,18 +549,11 @@ namespace Emby.Server.Implementations.HttpServer return new ServiceHandler(restPath, contentType); } - Logger.LogError("Could not find handler for {PathInfo}", pathInfo); + _logger.LogError("Could not find handler for {PathInfo}", pathInfo); return null; } - private static Task Write(IResponse response, string text) - { - var bOutput = Encoding.UTF8.GetBytes(text); - response.OriginalResponse.ContentLength = bOutput.Length; - return response.OutputStream.WriteAsync(bOutput, 0, bOutput.Length); - } - - private void RedirectToSecureUrl(IHttpRequest httpReq, IResponse httpRes, string url) + private void RedirectToSecureUrl(IHttpRequest httpReq, HttpResponse httpRes, string url) { if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri)) { @@ -640,23 +563,11 @@ namespace Emby.Server.Implementations.HttpServer Scheme = "https" }; url = builder.Uri.ToString(); - - RedirectToUrl(httpRes, url); - } - else - { - RedirectToUrl(httpRes, url); } - } - public static void RedirectToUrl(IResponse httpRes, string url) - { - httpRes.StatusCode = 302; - httpRes.AddHeader("Location", url); + httpRes.Redirect(url); } - public ServiceController ServiceController { get; private set; } - /// <summary> /// Adds the rest handlers. /// </summary> @@ -672,9 +583,9 @@ namespace Emby.Server.Implementations.HttpServer var types = services.Select(r => r.GetType()); ServiceController.Init(this, types); - ResponseFilters = new Action<IRequest, IResponse, object>[] + ResponseFilters = new Action<IRequest, HttpResponse, object>[] { - new ResponseFilter(Logger).FilterResponse + new ResponseFilter(_logger).FilterResponse }; } @@ -685,22 +596,14 @@ namespace Emby.Server.Implementations.HttpServer foreach (var route in clone) { - routes.Add(new RouteAttribute(NormalizeEmbyRoutePath(route.Path), route.Verbs) + routes.Add(new RouteAttribute(NormalizeCustomRoutePath(_config.Configuration.BaseUrl, 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 - }); - - // needed because apps add /emby, and some users also add /emby, thereby double prefixing - routes.Add(new RouteAttribute(DoubleNormalizeEmbyRoutePath(route.Path), route.Verbs) + routes.Add(new RouteAttribute(NormalizeOldRoutePath(route.Path), route.Verbs) { Notes = route.Notes, Priority = route.Priority, @@ -741,8 +644,8 @@ namespace Emby.Server.Implementations.HttpServer return _socketListener.ProcessWebSocketRequest(context); } - //TODO Add Jellyfin Route Path Normalizer - private static string NormalizeEmbyRoutePath(string path) + // this method was left for compatibility with third party clients + private static string NormalizeOldRoutePath(string path) { if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase)) { @@ -752,44 +655,33 @@ namespace Emby.Server.Implementations.HttpServer return "emby/" + path; } - private static string NormalizeMediaBrowserRoutePath(string path) + private static string NormalizeCustomRoutePath(string baseUrl, string path) { if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase)) { - return "/mediabrowser" + path; + return "/" + baseUrl + path; } - return "mediabrowser/" + path; + return baseUrl + "/" + path; } - private static string DoubleNormalizeEmbyRoutePath(string path) + /// <inheritdoc /> + public void Dispose() { - if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase)) - { - return "/emby/emby" + path; - } - - return "emby/emby/" + path; + Dispose(true); + GC.SuppressFinalize(this); } - private bool _disposed; - private readonly object _disposeLock = new object(); - protected virtual void Dispose(bool disposing) { if (_disposed) return; - lock (_disposeLock) + if (disposing) { - if (_disposed) return; - - _disposed = true; - - if (disposing) - { - Stop(); - } + Stop(); } + + _disposed = true; } /// <summary> @@ -803,26 +695,17 @@ namespace Emby.Server.Implementations.HttpServer return Task.CompletedTask; } - Logger.LogDebug("Websocket message received: {0}", result.MessageType); + _logger.LogDebug("Websocket message received: {0}", result.MessageType); - var tasks = _webSocketListeners.Select(i => Task.Run(async () => + IEnumerable<Task> GetTasks() { - try + foreach (var x in _webSocketListeners) { - await i.ProcessMessage(result).ConfigureAwait(false); + yield return x.ProcessMessageAsync(result); } - catch (Exception ex) - { - Logger.LogError(ex, "{0} failed processing WebSocket message {1}", i.GetType().Name, result.MessageType ?? string.Empty); - } - })); - - return Task.WhenAll(tasks); - } + } - public void Dispose() - { - Dispose(true); + return Task.WhenAll(GetTasks()); } } } |
