|
|
|
@ -33,11 +33,6 @@ namespace Emby.Server.Implementations.Session
|
|
|
|
|
/// </summary>
|
|
|
|
|
private const float ForceKeepAliveFactor = 0.75f;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Lock used for accessing the KeepAlive cancellation token.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private readonly object _keepAliveLock = new object();
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The WebSocket watchlist.
|
|
|
|
|
/// </summary>
|
|
|
|
@ -55,7 +50,7 @@ namespace Emby.Server.Implementations.Session
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The KeepAlive cancellation token.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private CancellationTokenSource? _keepAliveCancellationToken;
|
|
|
|
|
private System.Timers.Timer _keepAlive;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Initializes a new instance of the <see cref="SessionWebSocketListener" /> class.
|
|
|
|
@ -71,12 +66,34 @@ namespace Emby.Server.Implementations.Session
|
|
|
|
|
_logger = logger;
|
|
|
|
|
_sessionManager = sessionManager;
|
|
|
|
|
_loggerFactory = loggerFactory;
|
|
|
|
|
_keepAlive = new System.Timers.Timer(TimeSpan.FromSeconds(WebSocketLostTimeout * IntervalFactor))
|
|
|
|
|
{
|
|
|
|
|
AutoReset = true,
|
|
|
|
|
Enabled = false
|
|
|
|
|
};
|
|
|
|
|
_keepAlive.Elapsed += KeepAliveSockets;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
StopKeepAlive();
|
|
|
|
|
if (_keepAlive is not null)
|
|
|
|
|
{
|
|
|
|
|
_keepAlive.Stop();
|
|
|
|
|
_keepAlive.Elapsed -= KeepAliveSockets;
|
|
|
|
|
_keepAlive.Dispose();
|
|
|
|
|
_keepAlive = null!;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lock (_webSocketsLock)
|
|
|
|
|
{
|
|
|
|
|
foreach (var webSocket in _webSockets)
|
|
|
|
|
{
|
|
|
|
|
webSocket.Closed -= OnWebSocketClosed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_webSockets.Clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -164,7 +181,7 @@ namespace Emby.Server.Implementations.Session
|
|
|
|
|
webSocket.Closed += OnWebSocketClosed;
|
|
|
|
|
webSocket.LastKeepAliveDate = DateTime.UtcNow;
|
|
|
|
|
|
|
|
|
|
StartKeepAlive();
|
|
|
|
|
_keepAlive.Start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Notify WebSocket about timeout
|
|
|
|
@ -186,66 +203,26 @@ namespace Emby.Server.Implementations.Session
|
|
|
|
|
{
|
|
|
|
|
lock (_webSocketsLock)
|
|
|
|
|
{
|
|
|
|
|
if (!_webSockets.Remove(webSocket))
|
|
|
|
|
{
|
|
|
|
|
_logger.LogWarning("WebSocket {0} not on watchlist.", webSocket);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
if (_webSockets.Remove(webSocket))
|
|
|
|
|
{
|
|
|
|
|
webSocket.Closed -= OnWebSocketClosed;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Starts the KeepAlive watcher.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void StartKeepAlive()
|
|
|
|
|
{
|
|
|
|
|
lock (_keepAliveLock)
|
|
|
|
|
{
|
|
|
|
|
if (_keepAliveCancellationToken is null)
|
|
|
|
|
{
|
|
|
|
|
_keepAliveCancellationToken = new CancellationTokenSource();
|
|
|
|
|
// Start KeepAlive watcher
|
|
|
|
|
_ = RepeatAsyncCallbackEvery(
|
|
|
|
|
KeepAliveSockets,
|
|
|
|
|
TimeSpan.FromSeconds(WebSocketLostTimeout * IntervalFactor),
|
|
|
|
|
_keepAliveCancellationToken.Token);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Stops the KeepAlive watcher.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void StopKeepAlive()
|
|
|
|
|
{
|
|
|
|
|
lock (_keepAliveLock)
|
|
|
|
|
{
|
|
|
|
|
if (_keepAliveCancellationToken is not null)
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_keepAliveCancellationToken.Cancel();
|
|
|
|
|
_keepAliveCancellationToken.Dispose();
|
|
|
|
|
_keepAliveCancellationToken = null;
|
|
|
|
|
_logger.LogWarning("WebSocket {0} not on watchlist.", webSocket);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lock (_webSocketsLock)
|
|
|
|
|
{
|
|
|
|
|
foreach (var webSocket in _webSockets)
|
|
|
|
|
if (_webSockets.Count == 0)
|
|
|
|
|
{
|
|
|
|
|
webSocket.Closed -= OnWebSocketClosed;
|
|
|
|
|
_keepAlive.Stop();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_webSockets.Clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Checks status of KeepAlive of WebSockets.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private async Task KeepAliveSockets()
|
|
|
|
|
private async void KeepAliveSockets(object? o, EventArgs? e)
|
|
|
|
|
{
|
|
|
|
|
List<IWebSocketConnection> inactive;
|
|
|
|
|
List<IWebSocketConnection> lost;
|
|
|
|
@ -291,11 +268,6 @@ namespace Emby.Server.Implementations.Session
|
|
|
|
|
RemoveWebSocket(webSocket);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_webSockets.Count == 0)
|
|
|
|
|
{
|
|
|
|
|
StopKeepAlive();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -310,29 +282,5 @@ namespace Emby.Server.Implementations.Session
|
|
|
|
|
new ForceKeepAliveMessage(WebSocketLostTimeout),
|
|
|
|
|
CancellationToken.None);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Runs a given async callback once every specified interval time, until cancelled.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="callback">The async callback.</param>
|
|
|
|
|
/// <param name="interval">The interval time.</param>
|
|
|
|
|
/// <param name="cancellationToken">The cancellation token.</param>
|
|
|
|
|
/// <returns>Task.</returns>
|
|
|
|
|
private async Task RepeatAsyncCallbackEvery(Func<Task> callback, TimeSpan interval, CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
while (!cancellationToken.IsCancellationRequested)
|
|
|
|
|
{
|
|
|
|
|
await callback().ConfigureAwait(false);
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
await Task.Delay(interval, cancellationToken).ConfigureAwait(false);
|
|
|
|
|
}
|
|
|
|
|
catch (TaskCanceledException)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|