private async Task WebSocketRequestHandler()

in Nodejs/Product/WebRole/WebSocketProxyBase.cs [95:196]


        private async Task WebSocketRequestHandler(AspNetWebSocketContext context)
        {
            Log("Accepted web socket request from {0}.", context.UserHostAddress);

            TaskCompletionSource<bool> tcs = null;
            if (!AllowConcurrentConnections)
            {
                tcs = new TaskCompletionSource<bool>();
                while (true)
                {
                    var currentSession = Interlocked.CompareExchange(ref _currentSession, tcs.Task, null);
                    if (currentSession == null)
                    {
                        break;
                    }
                    Log("Another session is active, waiting for completion.");
                    await currentSession;
                    Log("The other session completed, proceeding.");
                }
            }

            try
            {
                var webSocket = context.WebSocket;
                using (var tcpClient = new TcpClient("localhost", DebuggerPort))
                {
                    try
                    {
                        var stream = tcpClient.GetStream();
                        var cts = new CancellationTokenSource();

                        // Start the workers that copy data from one socket to the other in both directions, and wait until either
                        // completes. The workers are fully async, and so their loops are transparently interleaved when running.
                        // Usually end of session is caused by VS dropping its connection on detach, and so it will be
                        // CopyFromWebSocketToStream that returns first; but it can be the other one if debuggee process crashes.
                        Log("Starting copy workers.");
                        var copyFromStreamToWebSocketTask = CopyFromStreamToWebSocketWorker(stream, webSocket, cts.Token);
                        var copyFromWebSocketToStreamTask = CopyFromWebSocketToStreamWorker(webSocket, stream, cts.Token);
                        Task completedTask = null;
                        try
                        {
                            completedTask = await Task.WhenAny(copyFromStreamToWebSocketTask, copyFromWebSocketToStreamTask);
                        }
                        catch (IOException ex)
                        {
                            Log(ex);
                        }
                        catch (WebSocketException ex)
                        {
                            Log(ex);
                        }

                        // Now that one worker is done, try to gracefully terminate the other one by issuing a cancellation request.
                        // it is normally blocked on a read, and this will cancel it if possible, and throw OperationCanceledException.
                        Log("One of the workers completed, shutting down the remaining one.");
                        cts.Cancel();
                        try
                        {
                            await Task.WhenAny(Task.WhenAll(copyFromStreamToWebSocketTask, copyFromWebSocketToStreamTask), Task.Delay(1000));
                        }
                        catch (OperationCanceledException ex)
                        {
                            Log(ex);
                        }

                        // Try to gracefully close the websocket if it's still open - this is not necessary, but nice to have.
                        Log("Both workers shut down, trying to close websocket.");
                        try
                        {
                            await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None);
                        }
                        catch (WebSocketException ex)
                        {
                            Log(ex);
                        }
                    }
                    finally
                    {
                        // Gracefully close the TCP socket. This is crucial to avoid "Remote debugger already attached" problems.
                        Log("Shutting down TCP socket.");
                        try
                        {
                            tcpClient.Client.Shutdown(SocketShutdown.Both);
                            tcpClient.Client.Disconnect(false);
                        }
                        catch (SocketException ex)
                        {
                            Log(ex);
                        }
                        Log("All done!");
                    }
                }
            }
            finally
            {
                if (tcs != null)
                {
                    Volatile.Write(ref _currentSession, null);
                    tcs.SetResult(true);
                }
            }
        }