aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
diff options
context:
space:
mode:
authorJoshua M. Boniface <joshua@boniface.me>2019-08-19 14:57:48 -0400
committerGitHub <noreply@github.com>2019-08-19 14:57:48 -0400
commitd95c04787cc4486f4ea5caaef20f6ac407a3f84a (patch)
tree7e3193614c5a132ae63034c2bb7e07956a5670e6 /Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
parent3ba709fcc32d7255a2cb2466dde8c2479130a2bc (diff)
parentd99278da1dcac4d3c60739e864597aa01f916636 (diff)
Merge branch 'master' into h265
Diffstat (limited to 'Emby.Server.Implementations/HttpServer/HttpListenerHost.cs')
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpListenerHost.cs247
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());
}
}
}