diff options
12 files changed, 95 insertions, 64 deletions
diff --git a/Emby.Server.Implementations/HttpServer/WebSocketManager.cs b/Emby.Server.Implementations/HttpServer/WebSocketManager.cs index 71ece80a7..5518fb95a 100644 --- a/Emby.Server.Implementations/HttpServer/WebSocketManager.cs +++ b/Emby.Server.Implementations/HttpServer/WebSocketManager.cs @@ -4,7 +4,9 @@ using System; using System.Collections.Generic; using System.Net.WebSockets; using System.Threading.Tasks; -using Jellyfin.Data.Events; +using Emby.Server.Implementations.Session; +using Jellyfin.Api.WebSocketListeners; +using MediaBrowser.Controller; using MediaBrowser.Controller.Net; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; @@ -13,24 +15,21 @@ namespace Emby.Server.Implementations.HttpServer { public class WebSocketManager : IWebSocketManager { - private readonly Lazy<IEnumerable<IWebSocketListener>> _webSocketListeners; + private readonly IServerApplicationHost _appHost; private readonly ILogger<WebSocketManager> _logger; private readonly ILoggerFactory _loggerFactory; - private bool _disposed = false; public WebSocketManager( - Lazy<IEnumerable<IWebSocketListener>> webSocketListeners, + IServerApplicationHost appHost, ILogger<WebSocketManager> logger, ILoggerFactory loggerFactory) { - _webSocketListeners = webSocketListeners; + _appHost = appHost; _logger = logger; _loggerFactory = loggerFactory; } - public event EventHandler<GenericEventArgs<IWebSocketConnection>> WebSocketConnected; - /// <inheritdoc /> public async Task WebSocketRequestHandler(HttpContext context) { @@ -39,6 +38,8 @@ namespace Emby.Server.Implementations.HttpServer return; } + var listener = _appHost.Resolve<ISessionWebSocketListener>(); + try { _logger.LogInformation("WS {IP} request", context.Connection.RemoteIpAddress); @@ -54,7 +55,7 @@ namespace Emby.Server.Implementations.HttpServer OnReceive = ProcessWebSocketMessageReceived }; - WebSocketConnected?.Invoke(this, new GenericEventArgs<IWebSocketConnection>(connection)); + listener?.ProcessWebSocketConnected(connection); await connection.ProcessAsync().ConfigureAwait(false); _logger.LogInformation("WS {IP} closed", context.Connection.RemoteIpAddress); @@ -80,16 +81,12 @@ namespace Emby.Server.Implementations.HttpServer return Task.CompletedTask; } - IEnumerable<Task> GetTasks() - { - var listeners = _webSocketListeners.Value; - foreach (var x in listeners) - { - yield return x.ProcessMessageAsync(result); - } - } + Parallel.Invoke( + () => _appHost.Resolve<IActivityLogWebSocketListener>(), + () => _appHost.Resolve<IScheduledTasksWebSocketListener>(), + () => _appHost.Resolve<ISessionInfoWebSocketListener>()); - return Task.WhenAll(GetTasks()); + return Task.CompletedTask; } } } diff --git a/Emby.Server.Implementations/Session/ISessionWebSocketListener.cs b/Emby.Server.Implementations/Session/ISessionWebSocketListener.cs new file mode 100644 index 000000000..9b0b28e6e --- /dev/null +++ b/Emby.Server.Implementations/Session/ISessionWebSocketListener.cs @@ -0,0 +1,30 @@ +namespace Emby.Server.Implementations.Session +{ + using System.Threading.Tasks; + using Jellyfin.Data.Events; + using MediaBrowser.Controller.Net; + + /// <summary> + /// Defines the <see cref="ISessionWebSocketListener" />. + /// </summary> + public interface ISessionWebSocketListener + { + /// <summary> + /// Runs processes due to a WebSocket connection event. + /// </summary> + /// <param name="websocketConnection">The <see cref="IWebSocketConnection"/> instance.</param> + void ProcessWebSocketConnected(IWebSocketConnection websocketConnection); + + /// <summary> + /// Disposes the object. + /// </summary> + void Dispose(); + + /// <summary> + /// Processes a message. + /// </summary> + /// <param name="message">The <see cref="WebSocketMessageInfo"/>.</param> + /// <returns>A <see cref="Task"/>.</returns> + Task ProcessMessageAsync(WebSocketMessageInfo message); + } +} diff --git a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs index a5f847953..8f81ee194 100644 --- a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs +++ b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs @@ -17,7 +17,7 @@ namespace Emby.Server.Implementations.Session /// <summary> /// Class SessionWebSocketListener. /// </summary> - public sealed class SessionWebSocketListener : IWebSocketListener, IDisposable + public sealed class SessionWebSocketListener : ISessionWebSocketListener, IDisposable { /// <summary> /// The timeout in seconds after which a WebSocket is considered to be lost. @@ -45,15 +45,13 @@ namespace Emby.Server.Implementations.Session private readonly ILogger<SessionWebSocketListener> _logger; private readonly ILoggerFactory _loggerFactory; - private readonly IWebSocketManager _webSocketManager; - /// <summary> /// The KeepAlive cancellation token. /// </summary> private CancellationTokenSource _keepAliveCancellationToken; /// <summary> - /// Lock used for accesing the KeepAlive cancellation token. + /// Lock used for accessing the KeepAlive cancellation token. /// </summary> private readonly object _keepAliveLock = new object(); @@ -63,7 +61,7 @@ namespace Emby.Server.Implementations.Session private readonly HashSet<IWebSocketConnection> _webSockets = new HashSet<IWebSocketConnection>(); /// <summary> - /// Lock used for accesing the WebSockets watchlist. + /// Lock used for accessing the WebSockets watchlist. /// </summary> private readonly object _webSocketsLock = new object(); @@ -73,32 +71,28 @@ namespace Emby.Server.Implementations.Session /// <param name="logger">The logger.</param> /// <param name="sessionManager">The session manager.</param> /// <param name="loggerFactory">The logger factory.</param> - /// <param name="webSocketManager">The HTTP server.</param> public SessionWebSocketListener( ILogger<SessionWebSocketListener> logger, ISessionManager sessionManager, - ILoggerFactory loggerFactory, - IWebSocketManager webSocketManager) + ILoggerFactory loggerFactory) { _logger = logger; _sessionManager = sessionManager; _loggerFactory = loggerFactory; - _webSocketManager = webSocketManager; - - webSocketManager.WebSocketConnected += OnServerManagerWebSocketConnected; } - private async void OnServerManagerWebSocketConnected(object sender, GenericEventArgs<IWebSocketConnection> e) + /// <inheritdoc/> + public async void ProcessWebSocketConnected(IWebSocketConnection websocketConnection) { - var session = GetSession(e.Argument.QueryString, e.Argument.RemoteEndPoint.ToString()); + var session = GetSession(websocketConnection.QueryString, websocketConnection.RemoteEndPoint.ToString()); if (session != null) { - EnsureController(session, e.Argument); - await KeepAliveWebSocket(e.Argument).ConfigureAwait(false); + EnsureController(session, websocketConnection); + await KeepAliveWebSocket(websocketConnection).ConfigureAwait(false); } else { - _logger.LogWarning("Unable to determine session based on query string: {0}", e.Argument.QueryString); + _logger.LogWarning("Unable to determine session based on query string: {Querystring}", websocketConnection.QueryString); } } @@ -122,7 +116,6 @@ namespace Emby.Server.Implementations.Session /// <inheritdoc /> public void Dispose() { - _webSocketManager.WebSocketConnected -= OnServerManagerWebSocketConnected; StopKeepAlive(); } diff --git a/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs b/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs index 288e03fcf..3dc45b145 100644 --- a/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs +++ b/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs @@ -11,7 +11,7 @@ namespace Jellyfin.Api.WebSocketListeners /// <summary> /// Class SessionInfoWebSocketListener. /// </summary> - public class ActivityLogWebSocketListener : BasePeriodicWebSocketListener<ActivityLogEntry[], WebSocketListenerState> + public class ActivityLogWebSocketListener : BasePeriodicWebSocketListener<ActivityLogEntry[], WebSocketListenerState>, IActivityLogWebSocketListener { /// <summary> /// The _kernel. diff --git a/Jellyfin.Api/WebSocketListeners/IActivityLogWebSocketListener.cs b/Jellyfin.Api/WebSocketListeners/IActivityLogWebSocketListener.cs new file mode 100644 index 000000000..b23022e71 --- /dev/null +++ b/Jellyfin.Api/WebSocketListeners/IActivityLogWebSocketListener.cs @@ -0,0 +1,10 @@ +#pragma warning disable CA1040 // Avoid empty interfaces +namespace Jellyfin.Api.WebSocketListeners +{ + /// <summary> + /// Defines the <see cref="IActivityLogWebSocketListener" />. + /// </summary> + public interface IActivityLogWebSocketListener + { + } +} diff --git a/Jellyfin.Api/WebSocketListeners/IScheduledTasksWebSocketListener.cs b/Jellyfin.Api/WebSocketListeners/IScheduledTasksWebSocketListener.cs new file mode 100644 index 000000000..425d61dfa --- /dev/null +++ b/Jellyfin.Api/WebSocketListeners/IScheduledTasksWebSocketListener.cs @@ -0,0 +1,10 @@ +#pragma warning disable CA1040 // Avoid empty interfaces +namespace Jellyfin.Api.WebSocketListeners +{ + /// <summary> + /// Defines the <see cref="IScheduledTasksWebSocketListener" />. + /// </summary> + public interface IScheduledTasksWebSocketListener + { + } +} diff --git a/Jellyfin.Api/WebSocketListeners/ISessionInfoWebSocketListener.cs b/Jellyfin.Api/WebSocketListeners/ISessionInfoWebSocketListener.cs new file mode 100644 index 000000000..33636dc06 --- /dev/null +++ b/Jellyfin.Api/WebSocketListeners/ISessionInfoWebSocketListener.cs @@ -0,0 +1,10 @@ +#pragma warning disable CA1040 // Avoid empty interfaces +namespace Jellyfin.Api.WebSocketListeners +{ + /// <summary> + /// Defines the <see cref="ISessionInfoWebSocketListener" />. + /// </summary> + public interface ISessionInfoWebSocketListener + { + } +} diff --git a/Jellyfin.Api/WebSocketListeners/ScheduledTasksWebSocketListener.cs b/Jellyfin.Api/WebSocketListeners/ScheduledTasksWebSocketListener.cs index 94df23e56..eafa9c71f 100644 --- a/Jellyfin.Api/WebSocketListeners/ScheduledTasksWebSocketListener.cs +++ b/Jellyfin.Api/WebSocketListeners/ScheduledTasksWebSocketListener.cs @@ -12,7 +12,7 @@ namespace Jellyfin.Api.WebSocketListeners /// <summary> /// Class ScheduledTasksWebSocketListener. /// </summary> - public class ScheduledTasksWebSocketListener : BasePeriodicWebSocketListener<IEnumerable<TaskInfo>, WebSocketListenerState> + public class ScheduledTasksWebSocketListener : BasePeriodicWebSocketListener<IEnumerable<TaskInfo>, WebSocketListenerState>, IScheduledTasksWebSocketListener { /// <summary> /// Gets or sets the task manager. @@ -66,19 +66,19 @@ namespace Jellyfin.Api.WebSocketListeners private void OnTaskCompleted(object? sender, TaskCompletionEventArgs e) { - SendData(true); + SendData(true).GetAwaiter().GetResult(); e.Task.TaskProgress -= OnTaskProgress; } private void OnTaskExecuting(object? sender, GenericEventArgs<IScheduledTaskWorker> e) { - SendData(true); + SendData(true).GetAwaiter().GetResult(); e.Argument.TaskProgress += OnTaskProgress; } private void OnTaskProgress(object? sender, GenericEventArgs<double> e) { - SendData(false); + SendData(false).GetAwaiter().GetResult(); } } } diff --git a/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs b/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs index d996ac69f..be701be45 100644 --- a/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs +++ b/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs @@ -11,7 +11,7 @@ namespace Jellyfin.Api.WebSocketListeners /// <summary> /// Class SessionInfoWebSocketListener. /// </summary> - public class SessionInfoWebSocketListener : BasePeriodicWebSocketListener<IEnumerable<SessionInfo>, WebSocketListenerState> + public class SessionInfoWebSocketListener : BasePeriodicWebSocketListener<IEnumerable<SessionInfo>, WebSocketListenerState>, ISessionInfoWebSocketListener { private readonly ISessionManager _sessionManager; diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index 4b44537a5..100f3619a 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -11,7 +11,6 @@ using Jellyfin.Server.Implementations; using Jellyfin.Server.Implementations.Activity; using Jellyfin.Server.Implementations.Events; using Jellyfin.Server.Implementations.Users; -using MediaBrowser.Common.Net; using MediaBrowser.Controller; using MediaBrowser.Controller.BaseItemManager; using MediaBrowser.Controller.Drawing; @@ -82,14 +81,10 @@ namespace Jellyfin.Server ServiceCollection.AddSingleton<IUserManager, UserManager>(); ServiceCollection.AddSingleton<IDisplayPreferencesManager, DisplayPreferencesManager>(); - ServiceCollection.AddScoped<IWebSocketListener, ActivityLogWebSocketListener>(); - ServiceCollection.AddScoped<IWebSocketListener, ScheduledTasksWebSocketListener>(); - ServiceCollection.AddScoped<IWebSocketListener, SessionInfoWebSocketListener>(); - // This one has to be last as DI will select it for parameterization. - ServiceCollection.AddScoped<IWebSocketListener, SessionWebSocketListener>(); - - // TODO fix circular dependency on IWebSocketManager - ServiceCollection.AddScoped(serviceProvider => new Lazy<IEnumerable<IWebSocketListener>>(serviceProvider.GetRequiredService<IEnumerable<IWebSocketListener>>)); + ServiceCollection.AddScoped<IActivityLogWebSocketListener, ActivityLogWebSocketListener>(); + ServiceCollection.AddScoped<IScheduledTasksWebSocketListener, ScheduledTasksWebSocketListener>(); + ServiceCollection.AddScoped<ISessionInfoWebSocketListener, SessionInfoWebSocketListener>(); + ServiceCollection.AddScoped<ISessionWebSocketListener, SessionWebSocketListener>(); base.RegisterServices(); } diff --git a/Jellyfin.Server/Middleware/WebSocketHandlerMiddleware.cs b/Jellyfin.Server/Middleware/WebSocketHandlerMiddleware.cs index 53f32bebd..ba0abfbdd 100644 --- a/Jellyfin.Server/Middleware/WebSocketHandlerMiddleware.cs +++ b/Jellyfin.Server/Middleware/WebSocketHandlerMiddleware.cs @@ -25,19 +25,10 @@ namespace Jellyfin.Server.Middleware /// </summary> /// <param name="httpContext">The current HTTP context.</param> /// <param name="webSocketManager">The WebSocket connection manager.</param> - /// <param name="websocketListener">Session manager instance.</param> /// <returns>The async task.</returns> public async Task Invoke( HttpContext httpContext, - IWebSocketManager webSocketManager, -#pragma warning disable CA1801 -#pragma warning disable IDE0060 - // TODO: Workaround. see https://github.com/jellyfin/jellyfin/pull/3194 - // Do not remove this parameter. It uses DI to create a SessionWebSocketListener which is - // required for webSocketManager events. - IWebSocketListener websocketListener) -#pragma warning restore IDE0060 // Remove unused parameter -#pragma warning restore CA1801 + IWebSocketManager webSocketManager) { if (!httpContext.WebSockets.IsWebSocketRequest) { diff --git a/MediaBrowser.Controller/Net/IWebSocketManager.cs b/MediaBrowser.Controller/Net/IWebSocketManager.cs index ce74173e7..d3f31de7c 100644 --- a/MediaBrowser.Controller/Net/IWebSocketManager.cs +++ b/MediaBrowser.Controller/Net/IWebSocketManager.cs @@ -12,11 +12,6 @@ namespace MediaBrowser.Controller.Net public interface IWebSocketManager { /// <summary> - /// Occurs when [web socket connected]. - /// </summary> - event EventHandler<GenericEventArgs<IWebSocketConnection>> WebSocketConnected; - - /// <summary> /// The HTTP request handler. /// </summary> /// <param name="context">The current HTTP context.</param> |
