public override async Task Handle(HttpContext context)
        {
            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://"))
                {
                    fullUrl = "ws://" + fullUrl.Substring(7);
                }
                else if (fullUrl.StartsWith("https://"))
                {
                    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.
                Uri wsUri = null;

                if (!Uri.TryCreate(fullUrl, UriKind.RelativeOrAbsolute, out wsUri))
                {
                    LoggerProxy.Default.Error("Failed to parse websocket URI.");
                    return;
                }

                string subProtocol = context.Request.Headers[Constants.Headers.SecWebSocketProtocol];
                var    wsServer    = new ClientWebSocket();

                if (subProtocol != null && subProtocol.Length > 0)
                {
                    wsServer.Options.AddSubProtocol(subProtocol);
                }

                wsServer.Options.Cookies = new System.Net.CookieContainer();

                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() });
                }


                LoggerProxy.Default.Info(string.Format("Connecting websocket to {0}", wsUri.AbsoluteUri));

                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 (!ForbiddenHttpHeaders.IsForbidden(hdr.Key))
                    {
                        reqHeaderBuilder.AppendFormat("{0}: {1}\r\n", hdr.Key, hdr.Value.ToString());

                        try
                        {
                            if (!ForbiddenWsHeaders.IsForbidden(hdr.Key))
                            {
                                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");

                diagSession.ServerRequestHeaders = reqHeaderBuilder.ToString();

                string serverSubProtocol = null;

                await wsServer.ConnectAsync(wsUri, context.RequestAborted);

                if (wsServer.State == System.Net.WebSockets.WebSocketState.Open)
                {
                    serverSubProtocol = wsServer.SubProtocol;
                }
                else
                {
                }

                // Create, via acceptor, the client websocket. This is the local machine's websocket.
                var wsClient = await context.WebSockets.AcceptWebSocketAsync(serverSubProtocol);

                /*
                 * TODO - Much of this is presently lost to us because the socket
                 * we get from AcceptWebSocketAsync is a mostly internal implementation
                 * that is NOT a ClientWebSocket.
                 *
                 * Ideally we would xfer all such properties from the client to our proxy
                 * client socket.
                 *
                 * wsServer.Options.ClientCertificates = wsClient.Options.ClientCertificates;
                 * wsServer.Options.Cookies = wsClient.Options.Cookies;
                 * wsServer.Options.Credentials = wsClient.Options.Credentials;
                 * wsServer.Options.KeepAliveInterval = wsClient.Options.KeepAliveInterval;
                 * wsServer.Options.Proxy = wsClient.Options.Proxy;
                 * wsServer.Options.UseDefaultCredentials = wsClient.Options.UseDefaultCredentials;
                 */

                LoggerProxy.Default.Info(string.Format("Connecting websocket to {0}", wsUri.AbsoluteUri));

                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() =>
                {
                    var serverBuffer = new byte[1024 * 4];
                    var serverStatus = await wsServer.ReceiveAsync(new ArraySegment <byte>(serverBuffer), context.RequestAborted);

                    while (!serverStatus.CloseStatus.HasValue && !wsClient.CloseStatus.HasValue && !context.RequestAborted.IsCancellationRequested)
                    {
                        await wsClient.SendAsync(new ArraySegment <byte>(serverBuffer, 0, serverStatus.Count), serverStatus.MessageType, serverStatus.EndOfMessage, context.RequestAborted);

                        serverStatus = await wsServer.ReceiveAsync(new ArraySegment <byte>(serverBuffer), context.RequestAborted);
                    }

                    await wsServer.CloseAsync(serverStatus.CloseStatus.Value, serverStatus.CloseStatusDescription, context.RequestAborted);
                });

                // 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() =>
                {
                    var clientBuffer = new byte[1024 * 4];
                    var clientResult = await wsClient.ReceiveAsync(new ArraySegment <byte>(clientBuffer), context.RequestAborted);

                    while (!clientResult.CloseStatus.HasValue && !wsServer.CloseStatus.HasValue && !context.RequestAborted.IsCancellationRequested)
                    {
                        await wsServer.SendAsync(new ArraySegment <byte>(clientBuffer, 0, clientResult.Count), clientResult.MessageType, clientResult.EndOfMessage, context.RequestAborted);

                        clientResult = await wsClient.ReceiveAsync(new ArraySegment <byte>(clientBuffer), context.RequestAborted);
                    }

                    await wsClient.CloseAsync(clientResult.CloseStatus.Value, clientResult.CloseStatusDescription, context.RequestAborted);
                });

                // 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);
            }
        }
        /// <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;
                }
            }
        }