using System; using System.Net.WebSockets; using System.Threading; using System.Threading.Tasks; using Emby.Server.Implementations.Net; using Microsoft.Extensions.Logging; namespace Jellyfin.Server.SocketSharp { public class SharpWebSocket : IWebSocket { /// /// The logger /// private readonly ILogger _logger; public event EventHandler Closed; /// /// Gets or sets the web socket. /// /// The web socket. private SocketHttpListener.WebSocket WebSocket { get; set; } private TaskCompletionSource _taskCompletionSource = new TaskCompletionSource(); private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); private bool _disposed = false; public SharpWebSocket(SocketHttpListener.WebSocket socket, ILogger logger) { if (socket == null) { throw new ArgumentNullException(nameof(socket)); } if (logger == null) { throw new ArgumentNullException(nameof(logger)); } _logger = logger; WebSocket = socket; socket.OnMessage += OnSocketMessage; socket.OnClose += OnSocketClose; socket.OnError += OnSocketError; WebSocket.ConnectAsServer(); } public Task StartReceive() { return _taskCompletionSource.Task; } private void OnSocketError(object sender, SocketHttpListener.ErrorEventArgs e) { _logger.LogError("Error in SharpWebSocket: {Message}", e.Message ?? string.Empty); // Closed?.Invoke(this, EventArgs.Empty); } private void OnSocketClose(object sender, SocketHttpListener.CloseEventArgs e) { _taskCompletionSource.TrySetResult(true); Closed?.Invoke(this, EventArgs.Empty); } private void OnSocketMessage(object sender, SocketHttpListener.MessageEventArgs e) { if (OnReceiveBytes != null) { OnReceiveBytes(e.RawData); } } /// /// Gets or sets the state. /// /// The state. public WebSocketState State => WebSocket.ReadyState; /// /// Sends the async. /// /// The bytes. /// if set to true [end of message]. /// The cancellation token. /// Task. public Task SendAsync(byte[] bytes, bool endOfMessage, CancellationToken cancellationToken) { return WebSocket.SendAsync(bytes); } /// /// Sends the asynchronous. /// /// The text. /// if set to true [end of message]. /// The cancellation token. /// Task. public Task SendAsync(string text, bool endOfMessage, CancellationToken cancellationToken) { return WebSocket.SendAsync(text); } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool dispose) { if (_disposed) { return; } if (dispose) { WebSocket.OnMessage -= OnSocketMessage; WebSocket.OnClose -= OnSocketClose; WebSocket.OnError -= OnSocketError; _cancellationTokenSource.Cancel(); WebSocket.Close(); } _disposed = true; } /// /// Gets or sets the receive action. /// /// The receive action. public Action OnReceiveBytes { get; set; } } }