private async Task HandleAfterSocketAsync (WebSocket nativeSocket, MariBaseWebSocketService service, HttpContext context) { using var socket = new MariWebSocket(nativeSocket, service.Cts.Token, service); _logger.LogDebug($"The new WebSocket has the Id: {socket.Id}"); service.AddClient(socket); await service.OnOpenAsync(socket, context) .Try(_logger, service, socket, false); try { await ReadAsync(socket, service, context); } catch (Exception ex) { if (ex is TaskCanceledException) { return; } await service.OnDisconnectedAsync(socket, WebSocketCloseStatus.ProtocolError, ex.Message) .Try(_logger, service, socket, false); if (socket.WebSocket.State.Equals(WebSocketState.Open)) { await socket.WebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closed by remote", service.Cts.Token) .Try(_logger, service, socket, false); } } nativeSocket.Dispose(); }
private static async Task HandleExceptionFromTryAsync<T> (Exception ex, ILogger<T> logger, MariBaseWebSocketService service, MariWebSocket socket, bool cancel) { await service.OnErrorAsync(ex, socket); if (cancel) throw ex; else if (logger.HasContent()) logger.LogError(ex, ex.Message); }
private async Task ReadAsync (MariWebSocket socket, MariBaseWebSocketService service, HttpContext context) { while (socket.WebSocket.State == WebSocketState.Open) { var buffer = new byte[service.BufferLength]; var result = await socket.WebSocket.ReceiveAsync(buffer, service.Cts.Token) .Try(_logger, service, socket); if (result.HasNoContent()) { continue; } await ReadMessageAsync(result, buffer, service, socket, context); } }
private async Task ReadMessageAsync (WebSocketReceiveResult result, byte[] buffer, MariBaseWebSocketService service, MariWebSocket socket, HttpContext context) { if (!result.EndOfMessage) { return; } _logger.LogTrace($"Incoming WebSocket message from " + $"{context.Connection.RemoteIpAddress}:{context.Connection.RemotePort}."); _logger.LogDebug($"Received WebSocket message from client's id: {socket.Id}"); if (result.MessageType.Equals(WebSocketMessageType.Text)) { await service.OnMessageAsync(socket, Encoding.UTF8.GetString(buffer)) .Try(_logger, service, socket, false); } else if (result.MessageType.Equals(WebSocketMessageType.Close)) { _logger.LogTrace($"Incoming WebSocket disconnect from " + $"{context.Connection.RemoteIpAddress}:{context.Connection.RemotePort}."); _logger.LogDebug($"WebSocket with id {socket.Id} disconnected."); await service.OnDisconnectedAsync( socket, result.CloseStatus.Value, Encoding.UTF8.GetString(buffer)) .Try(_logger, service, socket, false); service.RemoveClient(socket); await socket.WebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closed by remote", service.Cts.Token) .Try(_logger, service, socket, false); socket.Dispose(); } }
internal MessageEventArgs(MariWebSocket webSocket, string message) { WebSocket = webSocket; Message = message; }
/// <summary> /// Indicates when a message is received. /// </summary> /// <param name="socket">The client who sent the message.</param> /// <param name="message">The message sent by the client.</param> /// <returns></returns> internal abstract Task OnMessageAsync(MariWebSocket socket, string message);
/// <summary> /// Indicates when a client disconnected. /// </summary> /// <param name="socket">The WebSocket client disconnected.</param> /// <param name="code">The code reason of the disconnect.</param> /// <param name="reason">The description of the close reason.</param> /// <returns></returns> internal abstract Task OnDisconnectedAsync (MariWebSocket socket, WebSocketCloseStatus code, string reason);
/// <summary> /// Indicates when an error occurs. /// </summary> /// <param name="exception">The Exception throwed.</param> /// <param name="socket">The <see cref="MariWebSocket"/> where the error ocurried, can be null.</param> /// <returns></returns> internal abstract Task OnErrorAsync(Exception exception, MariWebSocket socket);
/// <summary> /// Indicates when a WebSocket connection is successfully opened. /// </summary> /// <param name="webSocket">The <see cref="MariWebSocket"/> connected.</param> /// <param name="context">The context of the new connection.</param> /// <returns></returns> internal abstract Task OnOpenAsync(MariWebSocket webSocket, HttpContext context);
/// <summary> /// Try get a client with the specific id. /// </summary> /// <param name="id">The id of the <see cref="MariWebSocket"/>.</param> /// <param name="webSocket">The <see cref="MariWebSocket"/> instance.</param> /// <returns>A <see cref="bool"/> representing if this client exists or no.</returns> protected bool TryGetClient(string id, out MariWebSocket webSocket) => Sockets.TryGetValue(id, out webSocket);
internal void RemoveClient(MariWebSocket socket) => RemoveClient(socket.Id);
internal void AddClient(MariWebSocket socket) => Sockets.TryAdd(socket.Id, socket);
internal DisconnectEventArgs(MariWebSocket webSocket, WebSocketCloseStatus code, string reason) { WebSocket = webSocket; Code = code; Reason = reason; }
internal ErrorEventArgs(Exception exception, MariWebSocket socket) { Exception = exception; WebSocket = socket; }
internal OpenEventArgs(MariWebSocket socket, HttpContext context) { WebSocket = socket; Context = context; }
/// <summary> /// Try await the task and calls /// <see cref="MariBaseWebSocketService"/>#OnErroAsync /// if a exception throws. /// </summary> /// <typeparam name="T">The <see cref="ILogger"/> type.</typeparam> /// <param name="task">The <see cref="Task"/> that will await.</param> /// <param name="logger">The ASP.NET Core <see cref="ILogger"/> for the Middleware.</param> /// <param name="service">The Socketservice.</param> /// <param name="socket">The <see cref="MariWebSocket"/>.</param> /// <param name="cancel">A Boolen indicates if that Method will throw the exception or not.</param> /// <returns></returns> internal static async Task Try<T> (this Task task, ILogger<T> logger, MariBaseWebSocketService service, MariWebSocket socket, bool cancel = true) { await task .TryAsync((ex) => HandleExceptionFromTryAsync(ex, logger, service, socket, cancel)); }