< Summary - Jellyfin

Information
Class: Emby.Server.Implementations.Session.WebSocketController
Assembly: Emby.Server.Implementations
File(s): /srv/git/jellyfin/Emby.Server.Implementations/Session/WebSocketController.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 52
Coverable lines: 52
Total lines: 186
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 10
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
get_HasOpenSockets()100%210%
get_SupportsMediaControl()100%210%
get_IsSessionActive()100%210%
AddWebSocket(...)100%210%
SendMessage(...)0%4260%
Dispose()0%2040%

File(s)

/srv/git/jellyfin/Emby.Server.Implementations/Session/WebSocketController.cs

#LineLine coverage
 1#pragma warning disable CS1591
 2
 3using System;
 4using System.Collections.Generic;
 5using System.Linq;
 6using System.Net.WebSockets;
 7using System.Threading;
 8using System.Threading.Tasks;
 9using MediaBrowser.Controller.Net;
 10using MediaBrowser.Controller.Net.WebSocketMessages;
 11using MediaBrowser.Controller.Session;
 12using MediaBrowser.Model.Session;
 13using Microsoft.Extensions.Logging;
 14
 15namespace Emby.Server.Implementations.Session
 16{
 17    public sealed class WebSocketController : ISessionController, IAsyncDisposable, IDisposable
 18    {
 19        private readonly ILogger<WebSocketController> _logger;
 20        private readonly ISessionManager _sessionManager;
 21        private readonly SessionInfo _session;
 22
 23        private readonly List<IWebSocketConnection> _sockets;
 24        private readonly ReaderWriterLockSlim _socketsLock;
 25        private bool _disposed = false;
 26
 27        public WebSocketController(
 28            ILogger<WebSocketController> logger,
 29            SessionInfo session,
 30            ISessionManager sessionManager)
 31        {
 032            _logger = logger;
 033            _session = session;
 034            _sessionManager = sessionManager;
 035            _sockets = new();
 036            _socketsLock = new();
 037        }
 38
 39        private bool HasOpenSockets
 40        {
 41            get
 42            {
 043                ObjectDisposedException.ThrowIf(_disposed, this);
 44                try
 45                {
 046                    _socketsLock.EnterReadLock();
 047                    return _sockets.Any(i => i.State == WebSocketState.Open);
 48                }
 49                finally
 50                {
 051                    _socketsLock.ExitReadLock();
 052                }
 053            }
 54        }
 55
 56        /// <inheritdoc />
 057        public bool SupportsMediaControl => HasOpenSockets;
 58
 59        /// <inheritdoc />
 060        public bool IsSessionActive => HasOpenSockets;
 61
 62        public void AddWebSocket(IWebSocketConnection connection)
 63        {
 064            _logger.LogDebug("Adding websocket to session {Session}", _session.Id);
 065            ObjectDisposedException.ThrowIf(_disposed, this);
 66            try
 67            {
 068                _socketsLock.EnterWriteLock();
 069                _sockets.Add(connection);
 070                connection.Closed += OnConnectionClosed;
 071            }
 72            finally
 73            {
 074                _socketsLock.ExitWriteLock();
 075            }
 076        }
 77
 78        private async void OnConnectionClosed(object? sender, EventArgs e)
 79        {
 80            var connection = sender as IWebSocketConnection ?? throw new ArgumentException($"{nameof(sender)} is not of 
 81            _logger.LogDebug("Removing websocket from session {Session}", _session.Id);
 82            ObjectDisposedException.ThrowIf(_disposed, this);
 83            try
 84            {
 85                _socketsLock.EnterWriteLock();
 86                _sockets.Remove(connection);
 87                connection.Closed -= OnConnectionClosed;
 88            }
 89            finally
 90            {
 91                _socketsLock.ExitWriteLock();
 92            }
 93
 94            await _sessionManager.CloseIfNeededAsync(_session).ConfigureAwait(false);
 95        }
 96
 97        /// <inheritdoc />
 98        public Task SendMessage<T>(
 99            SessionMessageType name,
 100            Guid messageId,
 101            T data,
 102            CancellationToken cancellationToken)
 103        {
 0104            ObjectDisposedException.ThrowIf(_disposed, this);
 105            IWebSocketConnection? socket;
 106            try
 107            {
 0108                _socketsLock.EnterReadLock();
 0109                socket = _sockets.Where(i => i.State == WebSocketState.Open).MaxBy(i => i.LastActivityDate);
 0110            }
 111            finally
 112            {
 0113                _socketsLock.ExitReadLock();
 0114            }
 115
 0116            if (socket is null)
 117            {
 0118                return Task.CompletedTask;
 119            }
 120
 0121            return socket.SendAsync(
 0122                new OutboundWebSocketMessage<T>
 0123                {
 0124                    Data = data,
 0125                    MessageType = name,
 0126                    MessageId = messageId
 0127                },
 0128                cancellationToken);
 129        }
 130
 131        /// <inheritdoc />
 132        public void Dispose()
 133        {
 0134            if (_disposed)
 135            {
 0136                return;
 137            }
 138
 139            try
 140            {
 0141                _socketsLock.EnterWriteLock();
 0142                foreach (var socket in _sockets)
 143                {
 0144                    socket.Closed -= OnConnectionClosed;
 0145                    socket.Dispose();
 146                }
 147
 0148                _sockets.Clear();
 0149            }
 150            finally
 151            {
 0152                _socketsLock.ExitWriteLock();
 0153            }
 154
 0155            _socketsLock.Dispose();
 0156            _disposed = true;
 0157        }
 158
 159        public async ValueTask DisposeAsync()
 160        {
 161            if (_disposed)
 162            {
 163                return;
 164            }
 165
 166            try
 167            {
 168                _socketsLock.EnterWriteLock();
 169                foreach (var socket in _sockets)
 170                {
 171                    socket.Closed -= OnConnectionClosed;
 172                    await socket.DisposeAsync().ConfigureAwait(false);
 173                }
 174
 175                _sockets.Clear();
 176            }
 177            finally
 178            {
 179                _socketsLock.ExitWriteLock();
 180            }
 181
 182            _socketsLock.Dispose();
 183            _disposed = true;
 184        }
 185    }
 186}