static void ConfigureListCommand(CommandLineApplication hcCommand)
        {
            hcCommand.RelayCommand("list", (listCmd) =>
            {
                listCmd.Description          = "List HybridConnection(s)";
                var connectionStringArgument = listCmd.Argument("connectionString", "Relay ConnectionString");

                listCmd.OnExecute(async() =>
                {
                    string connectionString = ConnectionStringUtility.ResolveConnectionString(connectionStringArgument);
                    if (string.IsNullOrEmpty(connectionString))
                    {
                        TraceMissingArgument(connectionStringArgument.Name);
                        listCmd.ShowHelp();
                        return(1);
                    }

                    var connectionStringBuilder = new RelayConnectionStringBuilder(connectionString);
                    RelayTraceSource.TraceInfo($"Listing HybridConnections for {connectionStringBuilder.Endpoint.Host}");
                    RelayTraceSource.TraceInfo($"{"Path",-38} {"ListenerCount",-15} {"RequiresClientAuth",-20}");
                    var namespaceManager = new RelayNamespaceManager(connectionString);
                    IEnumerable <HybridConnectionDescription> hybridConnections = await namespaceManager.GetHybridConnectionsAsync();
                    foreach (var hybridConnection in hybridConnections)
                    {
                        RelayTraceSource.TraceInfo($"{hybridConnection.Path,-38} {hybridConnection.ListenerCount,-15} {hybridConnection.RequiresClientAuthorization}");
                    }

                    return(0);
                });
            });
        }
        static void ConfigureCreateCommand(CommandLineApplication hcCommand)
        {
            hcCommand.RelayCommand("create", (createCmd) =>
            {
                createCmd.Description = "Create a HybridConnection";

                var pathArgument             = createCmd.Argument("path", "HybridConnection path");
                var connectionStringArgument = createCmd.Argument("connectionString", "Relay ConnectionString");

                var requireClientAuthOption = createCmd.Option(
                    CommandStrings.RequiresClientAuthTemplate, CommandStrings.RequiresClientAuthDescription, CommandOptionType.SingleValue);

                createCmd.OnExecute(async() =>
                {
                    string connectionString = ConnectionStringUtility.ResolveConnectionString(connectionStringArgument);
                    if (string.IsNullOrEmpty(connectionString) || string.IsNullOrEmpty(pathArgument.Value))
                    {
                        TraceMissingArgument(string.IsNullOrEmpty(connectionString) ? connectionStringArgument.Name : pathArgument.Name);
                        createCmd.ShowHelp();
                        return(1);
                    }

                    var connectionStringBuilder = new RelayConnectionStringBuilder(connectionString);
                    RelayTraceSource.TraceInfo($"Creating HybridConnection '{pathArgument.Value}' in {connectionStringBuilder.Endpoint.Host}...");
                    var hcDescription = new HybridConnectionDescription(pathArgument.Value);
                    hcDescription.RequiresClientAuthorization = GetBoolOption(requireClientAuthOption, true);
                    var namespaceManager = new RelayNamespaceManager(connectionString);
                    await namespaceManager.CreateHybridConnectionAsync(hcDescription);
                    RelayTraceSource.TraceInfo($"Creating HybridConnection '{pathArgument.Value}' in {connectionStringBuilder.Endpoint.Host} succeeded");
                    return(0);
                });
            });
        }
        static void ConfigureDeleteCommand(CommandLineApplication hcCommand)
        {
            hcCommand.RelayCommand("delete", (deleteCmd) =>
            {
                deleteCmd.Description        = "Delete a HybridConnection";
                var pathArgument             = deleteCmd.Argument("path", "HybridConnection path");
                var connectionStringArgument = deleteCmd.Argument("connectionString", "Relay ConnectionString");

                deleteCmd.OnExecute(async() =>
                {
                    string connectionString = ConnectionStringUtility.ResolveConnectionString(connectionStringArgument);
                    if (string.IsNullOrEmpty(connectionString) || string.IsNullOrEmpty(pathArgument.Value))
                    {
                        TraceMissingArgument(string.IsNullOrEmpty(connectionString) ? connectionStringArgument.Name : pathArgument.Name);
                        deleteCmd.ShowHelp();
                        return(1);
                    }

                    var connectionStringBuilder = new RelayConnectionStringBuilder(connectionString);
                    RelayTraceSource.TraceInfo($"Deleting HybridConnection '{pathArgument.Value}' in {connectionStringBuilder.Endpoint.Host}...");
                    var namespaceManager = new RelayNamespaceManager(connectionString);
                    await namespaceManager.DeleteHybridConnectionAsync(pathArgument.Value);
                    RelayTraceSource.TraceInfo($"Deleting HybridConnection '{pathArgument.Value}' in {connectionStringBuilder.Endpoint.Host} succeeded");
                    return(0);
                });
            });
        }
        static async Task <bool> EnsureHybridConnectionExists(TraceSource traceSource, RelayConnectionStringBuilder connectionString, RelayNamespaceManager namespaceManager)
        {
            bool createdHybridConnection = false;

            try
            {
                traceSource.TraceVerbose($"Checking whether HybridConnection '{connectionString.EntityPath}' exists");
                if (!await namespaceManager.HybridConnectionExistsAsync(connectionString.EntityPath))
                {
                    traceSource.TraceEvent(TraceEventType.Information, (int)ConsoleColor.White, $"Creating HybridConnection '{connectionString.EntityPath}'");
                    createdHybridConnection = true;
                    await namespaceManager.CreateHybridConnectionAsync(new HybridConnectionDescription(connectionString.EntityPath));

                    traceSource.TraceInformation("Created");
                }
            }
            catch (Exception exception)
            {
                RelayTraceSource.TraceException(exception, $"Ensuring HybridConnection '{connectionString.EntityPath}' exists");
            }

            return(createdHybridConnection);
        }
        public static async Task <int> VerifyListenAsync(TraceSource traceSource, RelayConnectionStringBuilder connectionString, string responseBody, int responseChunkLength, HttpStatusCode statusCode, string statusDescription)
        {
            var  namespaceManager        = new RelayNamespaceManager(connectionString.ToString());
            bool createdHybridConnection = await EnsureHybridConnectionExists(traceSource, connectionString, namespaceManager);

            HybridConnectionListener listener = null;

            try
            {
                listener             = new HybridConnectionListener(connectionString.ToString());
                listener.Connecting += (s, e) => RelayTraceSource.TraceException(listener.LastError, TraceEventType.Warning, "HybridConnectionListener Re-Connecting");
                listener.Online     += (s, e) => RelayTraceSource.Instance.TraceEvent(TraceEventType.Information, (int)ConsoleColor.Green, "HybridConnectionListener is online");
                EventHandler offlineHandler = (s, e) => RelayTraceSource.TraceException(listener.LastError, "HybridConnectionListener is OFFLINE");
                listener.Offline += offlineHandler;

                var responseBytes = Encoding.UTF8.GetBytes(responseBody);
                listener.RequestHandler = async(context) =>
                {
                    try
                    {
                        var stopwatch = new Stopwatch();
                        LogHttpRequest(context, traceSource);
                        context.Response.StatusCode = statusCode;
                        if (statusDescription != null)
                        {
                            context.Response.StatusDescription = statusDescription;
                        }

                        context.Response.Headers[HttpResponseHeader.ContentType] = "text/html";
                        int bytesWritten = 0;
                        do
                        {
                            int countToWrite = Math.Min(responseBody.Length - bytesWritten, responseChunkLength);
                            stopwatch.Restart();
                            await context.Response.OutputStream.WriteAsync(responseBytes, bytesWritten, countToWrite);

                            bytesWritten += countToWrite;
                            traceSource.TraceEvent(TraceEventType.Verbose, 0, "Sent {0} bytes in {1} ms", countToWrite, stopwatch.ElapsedMilliseconds);
                        }while (bytesWritten < responseBody.Length);

                        stopwatch.Restart();
                        await context.Response.CloseAsync();

                        traceSource.TraceEvent(TraceEventType.Verbose, 0, "Close completed in {0} ms", stopwatch.ElapsedMilliseconds);
                    }
                    catch (Exception exception)
                    {
                        RelayTraceSource.TraceException(exception, $"RequestHandler Error");
                    }
                };

                traceSource.TraceInformation($"Opening {listener}");
                await listener.OpenAsync();

                RunAcceptPump(listener);
                traceSource.TraceInformation("Press <ENTER> to close the listener ");
                Console.ReadLine();

                traceSource.TraceInformation($"Closing {listener}");
                listener.Offline -= offlineHandler; // Avoid a spurious trace on expected shutdown.
                await listener.CloseAsync();

                traceSource.TraceInformation("Closed");
                return(0);
            }
            catch (Exception)
            {
                listener?.CloseAsync();
                throw;
            }
            finally
            {
                if (createdHybridConnection)
                {
                    try
                    {
                        traceSource.TraceEvent(TraceEventType.Information, (int)ConsoleColor.White, $"Deleting HybridConnection '{connectionString.EntityPath}'");
                        await namespaceManager.DeleteHybridConnectionAsync(connectionString.EntityPath);

                        traceSource.TraceInformation("Deleted");
                    }
                    catch (Exception exception)
                    {
                        RelayTraceSource.TraceException(exception, "Deleting HybridConnection");
                    }
                }
            }
        }
        public static async Task <int> RunTestsAsync(RelayConnectionStringBuilder connectionString, TraceSource traceSource)
        {
            if (string.IsNullOrEmpty(connectionString.EntityPath))
            {
                connectionString.EntityPath = HybridConnectionCommands.DefaultPath;
            }

            var namespaceManager = new RelayNamespaceManager(connectionString.ToString());

            namespaceManager.Settings.OperationTimeout = TimeSpan.FromSeconds(5);
            HybridConnectionListener listener = null;
            bool createdHybridConnection      = false;
            int  returnCode  = 0;
            var  testResults = new List <TestResult>();
            var  cts         = new CancellationTokenSource(TimeSpan.FromSeconds(70));

            try
            {
                createdHybridConnection = await EnsureHybridConnectionExists(traceSource, connectionString, namespaceManager);

                listener = new HybridConnectionListener(connectionString.ToString());
                traceSource.TraceInformation($"Opening {listener}");
                await listener.OpenAsync(cts.Token);

                traceSource.TraceInformation("Listener Opened");

                Uri hybridHttpUri = new Uri($"https://{connectionString.Endpoint.GetComponents(UriComponents.HostAndPort, UriFormat.SafeUnescaped)}/{connectionString.EntityPath}");
                var token         = await listener.TokenProvider.GetTokenAsync(hybridHttpUri.AbsoluteUri, TimeSpan.FromMinutes(20));

                using (var client = new HttpClient {
                    BaseAddress = hybridHttpUri
                })
                {
                    client.DefaultRequestHeaders.ExpectContinue = false;

                    await RunTestAsync("TestPostLargeRequestSmallResponse", null, testResults, () => TestPostLargeRequestSmallResponse(listener, token, client, traceSource));
                    await RunTestAsync("TestPostLargeRequestWithLargeResponse", null, testResults, () => TestPostLargeRequestWithLargeResponse(listener, token, client, traceSource));
                    await RunTestAsync("TestGetLargeResponse", null, testResults, () => TestGetLargeResponse(listener, token, client, traceSource));
                    await RunTestAsync("TestGetSmallResponse", null, testResults, () => TestGetSmallResponse(listener, token, client, traceSource));
                }

                await RunTestAsync("TestStreaming", null, testResults, () => TestStreaming(listener, connectionString, traceSource));

                returnCode = ReportTestResults(testResults);
            }
            catch (Exception exception)
            {
                traceSource.TraceError("FAILURE WHILE RUNNING TESTS");
                RelayTraceSource.TraceException(exception, nameof(HybridConnectionTests));
                returnCode = exception.HResult;
            }
            finally
            {
                cts.Dispose();
                var cleanupCancelSource = new CancellationTokenSource(TimeSpan.FromSeconds(10));
                try
                {
                    if (listener != null)
                    {
                        traceSource.TraceInformation($"Closing {listener}");
                        await listener.CloseAsync(cleanupCancelSource.Token);

                        traceSource.TraceInformation("Listener Closed");
                    }

                    if (createdHybridConnection)
                    {
                        traceSource.TraceEvent(TraceEventType.Information, (int)ConsoleColor.White, $"Deleting HybridConnection '{connectionString.EntityPath}'");
                        await namespaceManager.DeleteHybridConnectionAsync(connectionString.EntityPath);
                    }
                }
                catch (Exception cleanupException)
                {
                    traceSource.TraceWarning($"Error during cleanup: {cleanupException.GetType()}: {cleanupException.Message}");
                }
                finally
                {
                    cleanupCancelSource.Dispose();
                }
            }

            return(returnCode);
        }