using System; using System.Net.WebSockets; using System.Text; using System.Threading; using System.Threading.Tasks; using Infrastructure.Extensions; using Microsoft.AspNetCore.Http; namespace Infrastructure.WebSockets { public class WebSocketManager { private readonly RequestDelegate _next; public delegate void SendHandler(ArraySegment buffer, WebSocketMessageType messageType, Func condition); public static event SendHandler SendMessage; public WebSocketManager(RequestDelegate next) { _next = next; } [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "<¹ÒÆð>")] public async Task InvokeAsync(HttpContext context) { if (context is null) { throw new ArgumentNullException(nameof(context)); } if (context.Request.Path.StartsWithSegments("/ws", StringComparison.CurrentCulture)) { if (context.Request.Path.StartsWithSegments("/ws", StringComparison.CurrentCulture)) { if (context.WebSockets.IsWebSocketRequest) { try { var sec = "Sec-WebSocket-Protocol"; if (context.Request.Headers.ContainsKey(sec)) { context.Response.Headers.Add(sec, context.Request.Headers[sec]); } var webSocket = await context.WebSockets.AcceptWebSocketAsync().ConfigureAwait(true); var key = Guid.NewGuid(); var wrapper = new WebSocketWrapper { Context = context, WebSocket = webSocket }; SendMessage += wrapper.Send; var buffer = new byte[1024 * 4]; WebSocketReceiveResult result = null; while (webSocket.State == WebSocketState.Open) { result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None).ConfigureAwait(true); if (result == null || result.CloseStatus.HasValue || result.MessageType == WebSocketMessageType.Close) { break; } var message = Encoding.UTF8.GetString(buffer, 0, result.Count); wrapper.OnReceived(message); } SendMessage -= wrapper.Send; if (webSocket.State == WebSocketState.Open || webSocket.State == WebSocketState.CloseSent) { await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).ConfigureAwait(true); } } catch (Exception ex) { ex.PrintStack(); } } else { context.Response.StatusCode = 400; } } } else { await this._next(context).ConfigureAwait(true); } } public static async Task Send(ArraySegment data, WebSocketMessageType messageType, Func condition) { await Task.Run(() => { SendMessage?.Invoke(data, messageType, condition); }).ConfigureAwait(true); } } }