public static async Task HandleWebSocketRequestAsync(HttpContext context) { void BadRequest(string message) { context.Response.StatusCode = StatusCodes.Status400BadRequest; context.Response.ContentType = "text/plain; charset=utf-8"; using (var sw = new System.IO.StreamWriter(context.Response.Body)) { sw.WriteLine(message); } } // // Make sure we get a good ID // if (!context.Request.Query.TryGetValue("id", out var idValues)) { BadRequest("Missing `id`"); return; } var id = idValues.LastOrDefault(); if (id == null || id.Length != 32) { BadRequest("Invalid `id`"); return; } // // Clear old sessions // var toClear = pendingSessions.Where(x => (DateTime.UtcNow - x.Value.CreateTimeUtc) > SessionTimeout).ToList(); foreach (var c in toClear) { pendingSessions.TryRemove(c.Key, out var _); } // // Find the pending session // if (!pendingSessions.TryRemove(id, out var activeSession)) { BadRequest("Unknown `id`"); return; } // // Set the element's dimensions // if (!context.Request.Query.TryGetValue("w", out var wValues) || wValues.Count < 1) { BadRequest("Missing `w`"); return; } if (!context.Request.Query.TryGetValue("h", out var hValues) || hValues.Count < 1) { BadRequest("Missing `h`"); return; } var icult = System.Globalization.CultureInfo.InvariantCulture; if (!double.TryParse(wValues.Last(), System.Globalization.NumberStyles.Any, icult, out var w)) { w = 640; } if (!double.TryParse(hValues.Last(), System.Globalization.NumberStyles.Any, icult, out var h)) { h = 480; } // // OK, Run // var token = CancellationToken.None; System.Net.WebSockets.WebSocket webSocket = null; void Error(string m, Exception e) => activeSession?.Logger?.LogWarning(e, m); // // Create a new session and let it handle everything from here // try { webSocket = await context.WebSockets.AcceptWebSocketAsync("Goui").ConfigureAwait(false); var session = new Goui.WebSocketSession(webSocket, activeSession.Element, activeSession.DisposeElementAfterSession, w, h, Error, token); await session.RunAsync().ConfigureAwait(false); } catch (System.Net.WebSockets.WebSocketException ex) when(ex.WebSocketErrorCode == System.Net.WebSockets.WebSocketError.ConnectionClosedPrematurely) { // The remote party closed the WebSocket connection without completing the close handshake. } catch (Exception ex) { context.Abort(); activeSession?.Logger?.LogWarning(ex, "Web socket session failed"); } finally { webSocket?.Dispose(); } }
/// <summary> /// Invoked when this handler is determined to be the best suited to handle the supplied connection. /// </summary> /// <param name="context"> /// The HTTP context. /// </param> /// <returns> /// The handling task. /// </returns> public override async Task Handle(HttpContext context) { ClientWebSocket wsServer = null; System.Net.WebSockets.WebSocket wsClient = null; try { // First we need the URL for this connection, since it's been requested to be // upgraded to a websocket. var connFeature = context.Features.Get <IHttpRequestFeature>(); string fullUrl = string.Empty; if (connFeature != null && connFeature.RawTarget != null && !string.IsNullOrEmpty(connFeature.RawTarget) && !(string.IsNullOrWhiteSpace(connFeature.RawTarget))) { fullUrl = $"{context.Request.Scheme}://{context.Request.Host}{connFeature.RawTarget}"; } else { fullUrl = $"{context.Request.Scheme}://{context.Request.Host}{context.Request.Path}{context.Request.QueryString}"; } // Need to replate the scheme with appropriate websocket scheme. if (fullUrl.StartsWith("http://", StringComparison.OrdinalIgnoreCase)) { fullUrl = "ws://" + fullUrl.Substring(7); } else if (fullUrl.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) { fullUrl = "wss://" + fullUrl.Substring(8); } // Next we need to try and parse the URL as a URI, because the websocket client // requires this for connecting upstream. if (!Uri.TryCreate(fullUrl, UriKind.RelativeOrAbsolute, out Uri wsUri)) { LoggerProxy.Default.Error("Failed to parse websocket URI."); return; } // Create the websocket that's going to connect to the remote server. wsServer = new ClientWebSocket(); wsServer.Options.Cookies = new System.Net.CookieContainer(); //wsServer.Options.SetBuffer((int)ushort.MaxValue * 16, (int)ushort.MaxValue * 16); foreach (var proto in context.WebSockets.WebSocketRequestedProtocols) { wsServer.Options.AddSubProtocol(proto); } foreach (var hdr in context.Request.Headers) { if (!ForbiddenWsHeaders.IsForbidden(hdr.Key)) { try { wsServer.Options.SetRequestHeader(hdr.Key, hdr.Value.ToString()); } catch (Exception hdrException) { LoggerProxy.Default.Error(hdrException); } } } foreach (var cookie in context.Request.Cookies) { try { wsServer.Options.Cookies.Add(new Uri(fullUrl, UriKind.Absolute), new System.Net.Cookie(cookie.Key, System.Net.WebUtility.UrlEncode(cookie.Value))); } catch (Exception e) { LoggerProxy.Default.Error("Error while attempting to add websocket cookie."); LoggerProxy.Default.Error(e); } } if (context.Connection.ClientCertificate != null) { wsServer.Options.ClientCertificates = new System.Security.Cryptography.X509Certificates.X509CertificateCollection(new[] { context.Connection.ClientCertificate.ToV2Certificate() }); } // Connect the server websocket to the upstream, remote webserver. await wsServer.ConnectAsync(wsUri, context.RequestAborted); foreach (string key in wsServer.ResponseHeaders) { if (!ForbiddenWsHeaders.IsForbidden(key)) { try { var value = wsServer.ResponseHeaders[key]; context.Response.Headers[key] = wsServer.ResponseHeaders[key]; } catch (Exception hdrException) { LoggerProxy.Default.Error(hdrException); } } } // Create, via acceptor, the client websocket. This is the local machine's websocket. wsClient = await context.WebSockets.AcceptWebSocketAsync(wsServer.SubProtocol ?? null); // Match the HTTP version of the client on the upstream request. We don't want to // transparently pass around headers that are wrong for the client's HTTP version. Version upstreamReqVersionMatch = null; Match match = s_httpVerRegex.Match(context.Request.Protocol); if (match != null && match.Success) { upstreamReqVersionMatch = Version.Parse(match.Value); } var msgNfo = new HttpMessageInfo { Url = wsUri, Method = new HttpMethod(context.Request.Method), IsEncrypted = context.Request.IsHttps, Headers = context.Request.Headers.ToNameValueCollection(), HttpVersion = upstreamReqVersionMatch ?? new Version(1, 0), MessageProtocol = MessageProtocol.WebSocket, MessageType = MessageType.Request, RemoteAddress = context.Connection.RemoteIpAddress, RemotePort = (ushort)context.Connection.RemotePort, LocalAddress = context.Connection.LocalIpAddress, LocalPort = (ushort)context.Connection.LocalPort }; _configuration.NewHttpMessageHandler?.Invoke(msgNfo); switch (msgNfo.ProxyNextAction) { case ProxyNextAction.DropConnection: { await wsClient.CloseAsync(System.Net.WebSockets.WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); return; } } var serverMessageInfo = new HttpMessageInfo { Url = wsUri, MessageId = msgNfo.MessageId, Method = new HttpMethod(context.Request.Method), IsEncrypted = context.Request.IsHttps, Headers = context.Request.Headers.ToNameValueCollection(), HttpVersion = upstreamReqVersionMatch ?? new Version(1, 0), MessageProtocol = MessageProtocol.WebSocket, MessageType = MessageType.Response, RemoteAddress = context.Connection.RemoteIpAddress, RemotePort = (ushort)context.Connection.RemotePort, LocalAddress = context.Connection.LocalIpAddress, LocalPort = (ushort)context.Connection.LocalPort }; var clientMessageInfo = new HttpMessageInfo { Url = wsUri, MessageId = msgNfo.MessageId, IsEncrypted = context.Request.IsHttps, Headers = context.Request.Headers.ToNameValueCollection(), HttpVersion = upstreamReqVersionMatch ?? new Version(1, 0), MessageProtocol = MessageProtocol.WebSocket, MessageType = MessageType.Request, RemoteAddress = context.Connection.RemoteIpAddress, RemotePort = (ushort)context.Connection.RemotePort, LocalAddress = context.Connection.LocalIpAddress, LocalPort = (ushort)context.Connection.LocalPort }; bool inspect = true; switch (msgNfo.ProxyNextAction) { case ProxyNextAction.AllowAndIgnoreContent: case ProxyNextAction.AllowAndIgnoreContentAndResponse: { inspect = false; } break; } // Spawn an async task that will poll the remote server for data in a loop, and then // write any data it gets to the client websocket. var serverTask = Task.Run(async() => { System.Net.WebSockets.WebSocketReceiveResult serverResult = null; var serverBuffer = new byte[1024 * 4]; try { bool looping = true; serverResult = await wsServer.ReceiveAsync(new ArraySegment <byte>(serverBuffer), context.RequestAborted); while (looping && !serverResult.CloseStatus.HasValue && !context.RequestAborted.IsCancellationRequested) { if (inspect) { serverMessageInfo.Body = new Memory <byte>(serverBuffer, 0, serverResult.Count); switch (serverResult.MessageType) { case System.Net.WebSockets.WebSocketMessageType.Binary: { serverMessageInfo.BodyContentType = s_octetStreamContentType; } break; case System.Net.WebSockets.WebSocketMessageType.Text: { serverMessageInfo.BodyContentType = s_plainTextContentType; } break; } _configuration.HttpMessageWholeBodyInspectionHandler?.Invoke(serverMessageInfo); } switch (serverMessageInfo.ProxyNextAction) { case ProxyNextAction.DropConnection: { looping = false; } break; default: { await wsClient.SendAsync(new ArraySegment <byte>(serverBuffer, 0, serverResult.Count), serverResult.MessageType, serverResult.EndOfMessage, context.RequestAborted); if (!wsClient.CloseStatus.HasValue) { serverResult = await wsServer.ReceiveAsync(new ArraySegment <byte>(serverBuffer), context.RequestAborted); continue; } } break; } looping = false; } await wsClient.CloseAsync(serverResult.CloseStatus.Value, serverResult.CloseStatusDescription, context.RequestAborted); } catch (Exception err) { LoggerProxy.Default.Error(err); try { var closeStatus = serverResult?.CloseStatus ?? System.Net.WebSockets.WebSocketCloseStatus.NormalClosure; var closeMessage = serverResult?.CloseStatusDescription ?? string.Empty; await wsClient.CloseAsync(closeStatus, closeMessage, context.RequestAborted); } catch { } } }); // Spawn an async task that will poll the local client websocket, in a loop, and then // write any data it gets to the remote server websocket. var clientTask = Task.Run(async() => { System.Net.WebSockets.WebSocketReceiveResult clientResult = null; var clientBuffer = new byte[1024 * 4]; try { bool looping = true; clientResult = await wsClient.ReceiveAsync(new ArraySegment <byte>(clientBuffer), context.RequestAborted); while (looping && !clientResult.CloseStatus.HasValue && !context.RequestAborted.IsCancellationRequested) { if (inspect) { clientMessageInfo.Body = new Memory <byte>(clientBuffer, 0, clientResult.Count); switch (clientResult.MessageType) { case System.Net.WebSockets.WebSocketMessageType.Binary: { clientMessageInfo.BodyContentType = s_octetStreamContentType; } break; case System.Net.WebSockets.WebSocketMessageType.Text: { clientMessageInfo.BodyContentType = s_plainTextContentType; } break; } _configuration.HttpMessageWholeBodyInspectionHandler?.Invoke(clientMessageInfo); } switch (clientMessageInfo.ProxyNextAction) { case ProxyNextAction.DropConnection: { looping = false; } break; default: { await wsServer.SendAsync(new ArraySegment <byte>(clientBuffer, 0, clientResult.Count), clientResult.MessageType, clientResult.EndOfMessage, context.RequestAborted); if (!wsServer.CloseStatus.HasValue) { clientResult = await wsClient.ReceiveAsync(new ArraySegment <byte>(clientBuffer), context.RequestAborted); continue; } } break; } looping = false; } await wsServer.CloseAsync(clientResult.CloseStatus.Value, clientResult.CloseStatusDescription, context.RequestAborted); } catch (Exception err) { LoggerProxy.Default.Error(err); try { var closeStatus = clientResult?.CloseStatus ?? System.Net.WebSockets.WebSocketCloseStatus.NormalClosure; var closeMessage = clientResult?.CloseStatusDescription ?? string.Empty; await wsServer.CloseAsync(closeStatus, closeMessage, context.RequestAborted); } catch { } } }); // Above, we have created a bridge between the local and remote websocket. Wait for // both associated tasks to complete. await Task.WhenAll(serverTask, clientTask); } catch (Exception wshe) { LoggerProxy.Default.Error(wshe); } finally { if (wsClient != null) { wsClient.Dispose(); wsClient = null; } if (wsServer != null) { wsServer.Dispose(); wsServer = null; } } }
protected override void Dispose(bool disposing) { cts.Cancel(); webSocket.Dispose(); base.Dispose(disposing); }
public override async Task Handle(HttpContext context) { ClientWebSocket wsServer = null; System.Net.WebSockets.WebSocket wsClient = null; try { // First we need the URL for this connection, since it's been requested to be upgraded to // a websocket. var fullUrl = Microsoft.AspNetCore.Http.Extensions.UriHelper.GetDisplayUrl(context.Request); // Need to replate the scheme with appropriate websocket scheme. if (fullUrl.StartsWith("http://")) { fullUrl = "ws://" + fullUrl.Substring(7); } else if (fullUrl.StartsWith("https://")) { fullUrl = "wss://" + fullUrl.Substring(8); } // Next we need to try and parse the URL as a URI, because the websocket client requires // this for connecting upstream. if (!Uri.TryCreate(fullUrl, UriKind.RelativeOrAbsolute, out Uri wsUri)) { LoggerProxy.Default.Error("Failed to parse websocket URI."); return; } // Create the websocket that's going to connect to the remote server. wsServer = new ClientWebSocket(); wsServer.Options.Cookies = new System.Net.CookieContainer(); wsServer.Options.SetBuffer((int)ushort.MaxValue * 16, (int)ushort.MaxValue * 16); foreach (var cookie in context.Request.Cookies) { try { wsServer.Options.Cookies.Add(new Uri(fullUrl, UriKind.Absolute), new System.Net.Cookie(cookie.Key, System.Net.WebUtility.UrlEncode(cookie.Value))); } catch (Exception e) { LoggerProxy.Default.Error("Error while attempting to add websocket cookie."); LoggerProxy.Default.Error(e); } } if (context.Connection.ClientCertificate != null) { wsServer.Options.ClientCertificates = new System.Security.Cryptography.X509Certificates.X509CertificateCollection(new[] { context.Connection.ClientCertificate.ToV2Certificate() }); } var reqHeaderBuilder = new StringBuilder(); foreach (var hdr in context.Request.Headers) { if (!ForbiddenWsHeaders.IsForbidden(hdr.Key)) { reqHeaderBuilder.AppendFormat("{0}: {1}\r\n", hdr.Key, hdr.Value.ToString()); try { wsServer.Options.SetRequestHeader(hdr.Key, hdr.Value.ToString()); Console.WriteLine("Set Header: {0} ::: {1}", hdr.Key, hdr.Value.ToString()); } catch (Exception hdrException) { Console.WriteLine("Failed Header: {0} ::: {1}", hdr.Key, hdr.Value.ToString()); LoggerProxy.Default.Error(hdrException); } } } reqHeaderBuilder.Append("\r\n"); LoggerProxy.Default.Info(string.Format("Connecting websocket to {0}", wsUri.AbsoluteUri)); // Connect the server websocket to the upstream, remote webserver. await wsServer.ConnectAsync(wsUri, context.RequestAborted); LoggerProxy.Default.Info(String.Format("Connected websocket to {0}", wsUri.AbsoluteUri)); // Create, via acceptor, the client websocket. This is the local machine's websocket. wsClient = await context.WebSockets.AcceptWebSocketAsync(wsServer.SubProtocol ?? null); ProxyNextAction nxtAction = ProxyNextAction.AllowAndIgnoreContentAndResponse; string customResponseContentType = string.Empty; byte[] customResponse = null; m_msgBeginCb?.Invoke(wsUri, reqHeaderBuilder.ToString(), null, context.Request.IsHttps ? MessageType.SecureWebSocket : MessageType.WebSocket, MessageDirection.Request, out nxtAction, out customResponseContentType, out customResponse); switch (nxtAction) { case ProxyNextAction.DropConnection: { if (customResponse != null) { } await wsClient.CloseAsync(System.Net.WebSockets.WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); return; } } // Spawn an async task that will poll the remote server for data in a loop, and then // write any data it gets to the client websocket. var serverTask = Task.Run(async() => { System.Net.WebSockets.WebSocketReceiveResult serverStatus = null; var serverBuffer = new byte[1024 * 4]; try { bool looping = true; serverStatus = await wsServer.ReceiveAsync(new ArraySegment <byte>(serverBuffer), context.RequestAborted); while (looping && !serverStatus.CloseStatus.HasValue && !context.RequestAborted.IsCancellationRequested) { await wsClient.SendAsync(new ArraySegment <byte>(serverBuffer, 0, serverStatus.Count), serverStatus.MessageType, serverStatus.EndOfMessage, context.RequestAborted); if (!wsClient.CloseStatus.HasValue) { serverStatus = await wsServer.ReceiveAsync(new ArraySegment <byte>(serverBuffer), context.RequestAborted); continue; } looping = false; } await wsClient.CloseAsync(serverStatus.CloseStatus.Value, serverStatus.CloseStatusDescription, context.RequestAborted); } catch { try { var closeStatus = serverStatus?.CloseStatus ?? System.Net.WebSockets.WebSocketCloseStatus.NormalClosure; var closeMessage = serverStatus?.CloseStatusDescription ?? string.Empty; await wsClient.CloseAsync(closeStatus, closeMessage, context.RequestAborted); } catch { } } }); // Spawn an async task that will poll the local client websocket, in a loop, and then // write any data it gets to the remote server websocket. var clientTask = Task.Run(async() => { System.Net.WebSockets.WebSocketReceiveResult clientResult = null; var clientBuffer = new byte[1024 * 4]; try { bool looping = true; clientResult = await wsClient.ReceiveAsync(new ArraySegment <byte>(clientBuffer), context.RequestAborted); while (looping && !clientResult.CloseStatus.HasValue && !context.RequestAborted.IsCancellationRequested) { await wsServer.SendAsync(new ArraySegment <byte>(clientBuffer, 0, clientResult.Count), clientResult.MessageType, clientResult.EndOfMessage, context.RequestAborted); if (!wsServer.CloseStatus.HasValue) { clientResult = await wsClient.ReceiveAsync(new ArraySegment <byte>(clientBuffer), context.RequestAborted); continue; } looping = false; } await wsServer.CloseAsync(clientResult.CloseStatus.Value, clientResult.CloseStatusDescription, context.RequestAborted); } catch { try { var closeStatus = clientResult?.CloseStatus ?? System.Net.WebSockets.WebSocketCloseStatus.NormalClosure; var closeMessage = clientResult?.CloseStatusDescription ?? string.Empty; await wsServer.CloseAsync(closeStatus, closeMessage, context.RequestAborted); } catch { } } }); // Above, we have created a bridge between the local and remote websocket. Wait for both // associated tasks to complete. await Task.WhenAll(serverTask, clientTask); } catch (Exception wshe) { if (wshe is System.Net.WebSockets.WebSocketException) { var cast = wshe as System.Net.WebSockets.WebSocketException; Console.WriteLine(cast.WebSocketErrorCode); if (cast.Data != null) { foreach (KeyValuePair <object, object> kvp in cast.Data) { Console.WriteLine("{0} ::: {1}", kvp.Key, kvp.Value); } } } LoggerProxy.Default.Error(wshe); } finally { if (wsClient != null) { wsClient.Dispose(); wsClient = null; } if (wsServer != null) { wsServer.Dispose(); wsServer = null; } } }
/// <summary> /// Invoked when this handler is determined to be the best suited to handle the supplied connection. /// </summary> /// <param name="context"> /// The HTTP context. /// </param> /// <returns> /// The handling task. /// </returns> public override async Task Handle(HttpContext context) { ClientWebSocket wsServer = null; System.Net.WebSockets.WebSocket wsClient = null; DiagnosticsWebSession diagSession = new DiagnosticsWebSession(); try { // First we need the URL for this connection, since it's been requested to be upgraded to // a websocket. var fullUrl = Microsoft.AspNetCore.Http.Extensions.UriHelper.GetDisplayUrl(context.Request); // Need to replate the scheme with appropriate websocket scheme. if (fullUrl.StartsWith("http://", StringComparison.OrdinalIgnoreCase)) { fullUrl = "ws://" + fullUrl.Substring(7); } else if (fullUrl.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) { fullUrl = "wss://" + fullUrl.Substring(8); } diagSession.ClientRequestUri = fullUrl; // Next we need to try and parse the URL as a URI, because the websocket client requires // this for connecting upstream. if (!Uri.TryCreate(fullUrl, UriKind.RelativeOrAbsolute, out Uri wsUri)) { LoggerProxy.Default.Error("Failed to parse websocket URI."); return; } // Create the websocket that's going to connect to the remote server. wsServer = new ClientWebSocket(); wsServer.Options.Cookies = new System.Net.CookieContainer(); wsServer.Options.SetBuffer((int)ushort.MaxValue * 16, (int)ushort.MaxValue * 16); foreach (var cookie in context.Request.Cookies) { try { wsServer.Options.Cookies.Add(new Uri(fullUrl, UriKind.Absolute), new System.Net.Cookie(cookie.Key, System.Net.WebUtility.UrlEncode(cookie.Value))); } catch (Exception e) { LoggerProxy.Default.Error("Error while attempting to add websocket cookie."); LoggerProxy.Default.Error(e); } } if (context.Connection.ClientCertificate != null) { wsServer.Options.ClientCertificates = new System.Security.Cryptography.X509Certificates.X509CertificateCollection(new[] { context.Connection.ClientCertificate.ToV2Certificate() }); } if (Collector.IsDiagnosticsEnabled) { var diagHeaderBuilder = new StringBuilder(); foreach (var hdr in context.Request.Headers) { diagHeaderBuilder.AppendFormat($"{hdr.Key}: {hdr.Value.ToString()}\r\n"); } diagSession.ClientRequestHeaders = diagHeaderBuilder.ToString(); } var reqHeaderBuilder = new StringBuilder(); foreach (var hdr in context.Request.Headers) { if (!ForbiddenWsHeaders.IsForbidden(hdr.Key)) { reqHeaderBuilder.AppendFormat("{0}: {1}\r\n", hdr.Key, hdr.Value.ToString()); try { wsServer.Options.SetRequestHeader(hdr.Key, hdr.Value.ToString()); } catch (Exception hdrException) { LoggerProxy.Default.Error(hdrException); } } } reqHeaderBuilder.Append("\r\n"); diagSession.ServerRequestHeaders = reqHeaderBuilder.ToString(); // Connect the server websocket to the upstream, remote webserver. await wsServer.ConnectAsync(wsUri, context.RequestAborted); // Create, via acceptor, the client websocket. This is the local machine's websocket. wsClient = await context.WebSockets.AcceptWebSocketAsync(wsServer.SubProtocol ?? null); var msgNfo = new HttpMessageInfo { Url = wsUri, IsEncrypted = context.Request.IsHttps, Headers = context.Request.Headers.ToNameValueCollection(), MessageProtocol = MessageProtocol.WebSocket, MessageType = MessageType.Request, RemoteAddress = context.Connection.RemoteIpAddress, RemotePort = (ushort)context.Connection.RemotePort, LocalAddress = context.Connection.LocalIpAddress, LocalPort = (ushort)context.Connection.LocalPort }; _newMessageCb?.Invoke(msgNfo); switch (msgNfo.ProxyNextAction) { case ProxyNextAction.DropConnection: { await wsClient.CloseAsync(System.Net.WebSockets.WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); return; } } // Spawn an async task that will poll the remote server for data in a loop, and then // write any data it gets to the client websocket. var serverTask = Task.Run(async() => { System.Net.WebSockets.WebSocketReceiveResult serverStatus = null; var serverBuffer = new byte[1024 * 4]; try { bool looping = true; serverStatus = await wsServer.ReceiveAsync(new ArraySegment <byte>(serverBuffer), context.RequestAborted); while (looping && !serverStatus.CloseStatus.HasValue && !context.RequestAborted.IsCancellationRequested) { await wsClient.SendAsync(new ArraySegment <byte>(serverBuffer, 0, serverStatus.Count), serverStatus.MessageType, serverStatus.EndOfMessage, context.RequestAborted); if (!wsClient.CloseStatus.HasValue) { serverStatus = await wsServer.ReceiveAsync(new ArraySegment <byte>(serverBuffer), context.RequestAborted); continue; } looping = false; } await wsClient.CloseAsync(serverStatus.CloseStatus.Value, serverStatus.CloseStatusDescription, context.RequestAborted); } catch { try { var closeStatus = serverStatus?.CloseStatus ?? System.Net.WebSockets.WebSocketCloseStatus.NormalClosure; var closeMessage = serverStatus?.CloseStatusDescription ?? string.Empty; await wsClient.CloseAsync(closeStatus, closeMessage, context.RequestAborted); } catch { } } }); // Spawn an async task that will poll the local client websocket, in a loop, and then // write any data it gets to the remote server websocket. var clientTask = Task.Run(async() => { System.Net.WebSockets.WebSocketReceiveResult clientResult = null; var clientBuffer = new byte[1024 * 4]; try { bool looping = true; clientResult = await wsClient.ReceiveAsync(new ArraySegment <byte>(clientBuffer), context.RequestAborted); while (looping && !clientResult.CloseStatus.HasValue && !context.RequestAborted.IsCancellationRequested) { await wsServer.SendAsync(new ArraySegment <byte>(clientBuffer, 0, clientResult.Count), clientResult.MessageType, clientResult.EndOfMessage, context.RequestAborted); if (!wsServer.CloseStatus.HasValue) { clientResult = await wsClient.ReceiveAsync(new ArraySegment <byte>(clientBuffer), context.RequestAborted); continue; } looping = false; } await wsServer.CloseAsync(clientResult.CloseStatus.Value, clientResult.CloseStatusDescription, context.RequestAborted); } catch { try { var closeStatus = clientResult?.CloseStatus ?? System.Net.WebSockets.WebSocketCloseStatus.NormalClosure; var closeMessage = clientResult?.CloseStatusDescription ?? string.Empty; await wsServer.CloseAsync(closeStatus, closeMessage, context.RequestAborted); } catch { } } }); // Above, we have created a bridge between the local and remote websocket. Wait for both // associated tasks to complete. await Task.WhenAll(serverTask, clientTask); } catch (Exception wshe) { LoggerProxy.Default.Error(wshe); } finally { Collector.ReportSession(diagSession); if (wsClient != null) { wsClient.Dispose(); wsClient = null; } if (wsServer != null) { wsServer.Dispose(); wsServer = null; } } }