Ngrok deploying the app online the result is always empty

I want to test grpc online via the internet. For that purpose I need tunneling my laptop, so that local grpc can be accessed via the internet with the help of ngrok program.

I downloaded ngrok.exe from https://dashboard.ngrok.com/get-started/setup/windows

The authentication process always succeeds when I tested with C# (Step 1),

but when deploying the app online the result is always empty (Step 2)?

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>using CardData.Ext.Logger;
using CustomerCardServer.Ext.Mod.General;
using CustomerCardServer.Properties;
using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;
using File = System.IO.File;
namespace CustomerCardServer.Ext.Mod.Tunnel
{
public class NgrokTunnelManager
{
private static NgrokTunnelManager? _instance;
private static object _instanceLocker = new object();
private String? _appPath;
private String? _ngrokTunnelFile;
private String? _cmdFile;
private List<int> _processIDs = new List<int>();
public List<int> ProcessIDs
{
get { return _processIDs; }
private set { _processIDs = value; }
}
public String? SessionStatus { get; private set; }
public String? PublicUrl { get; private set; }
public String? PrivateUrl { get; private set; }
private NgrokTunnelManager()
{
CopyCmdPromptFile();
CopyNgrokTunnelFile();
}
public static NgrokTunnelManager GetInstance()
{
if (_instance is null)
{
lock (_instanceLocker)
{
_instance = new NgrokTunnelManager();
}
}
return _instance;
}
public bool CopyNgrokTunnelFile()
{
try
{
// App path
_appPath = Application.StartupPath;
if (String.IsNullOrEmpty(_appPath))
{
MessageBox.Show("AppPath isn't valid!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
// ngrok file
_ngrokTunnelFile = _appPath + Common.NGROK_NAME;
if (String.IsNullOrEmpty(_ngrokTunnelFile))
{
MessageBox.Show("Devtunnel.exe isn't found!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
// create ngrok file
if (!File.Exists(_ngrokTunnelFile))
{
File.WriteAllBytes(_ngrokTunnelFile, Resources.ngrok);
}
// save ngrok file path
Settings.Default.NgrokFile = _ngrokTunnelFile;
Settings.Default.Save();
return true;
}
catch (Exception ex)
{
Log.Exception(ex);
}
return false;
}
public bool CopyCmdPromptFile()
{
try
{
// App path
_appPath = Application.StartupPath;
if (String.IsNullOrEmpty(_appPath))
{
MessageBox.Show("AppPath isn't valid!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
// cmd shortcut file
_cmdFile = _appPath + Common.CMD_NAME;
if (String.IsNullOrEmpty(_cmdFile))
{
MessageBox.Show("Cmd.exe isn't found!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
// create cmd shortcut file
if (!File.Exists(_cmdFile))
{
File.WriteAllBytes(_cmdFile, Resources.cmd);
}
// save cmd shortcut file path
Settings.Default.CmdFile = _cmdFile;
Settings.Default.Save();
return true;
}
catch (Exception ex)
{
Log.Exception(ex);
}
return false;
}
public async Task<bool> AuthAlsoStartAsync(String authToken, String ipAddress, int port)
{
try
{
var argument1 = $"ngrok config add-authtoken {authToken}";
var argument2 = $"ngrok http https://{ipAddress}:{port}";
var authResult = await RunCommandAsync(argument1, TimeSpan.FromSeconds(5));
if (!authResult.Contains("Authtoken saved to configuration file"))
{
return false;
}
// BUG IN HERE, startResult IS ALWAYS EMPTY?
var startResult = await RunCommandAsync(argument2, TimeSpan.FromSeconds(5));
if (String.IsNullOrEmpty(startResult))
{
return false;
}
// Regex to extract Session Status
String? sessionStatus = Regex.Match(startResult, @"Session Statuss+(w+)").Groups[1].Value;
// Regex to extract Forwarding URLs
Match? forwardingMatch = Regex.Match(startResult, @"Forwardings+(https?://[^s]+)s+->s+(https?://[^s]+)");
String? publicUrl = forwardingMatch.Groups[1].Value;
String? privateUrl = forwardingMatch.Groups[2].Value;
if (!String.IsNullOrEmpty(sessionStatus) && !String.IsNullOrEmpty(publicUrl) && !String.IsNullOrEmpty(privateUrl))
{
SessionStatus = sessionStatus;
PublicUrl = publicUrl;
PrivateUrl = privateUrl;
return true;
}
}
catch (Exception ex)
{
Log.Exception(ex);
}
return false;
}
private int GetNgrokTunnelProcessId(string processName)
{
try
{
// Get the list of processes with the specified name
Process[] processes = Process.GetProcessesByName(processName);
if (processes.Length > 0)
{
// Return the PID of the first process found
return processes[0].Id;
}
return -1; // Process not found
}
catch (Exception ex)
{
Log.Exception(ex);
}
return -1;
}
private void StopProcess()
{
try
{
// kill current devtunnel
Process[] processes = Process.GetProcessesByName(Path.GetFileNameWithoutExtension(Common.NGROK_NAME));
foreach (Process process in processes)
{
if (process is null)
{
continue;
}
if (_processIDs is null)
{
break;
}
if (_processIDs.Count == 0)
{
break;
}
if (_processIDs.Contains(process.Id))
{
process.Kill();
}
}
}
catch (Exception ex)
{
Log.Exception(ex);
}
}
public async Task<String> RunCommandAsync(String argument, TimeSpan timeout, bool isInputEnable = false)
{
try
{
StopProcess();
var startInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = $"/c {argument}",
RedirectStandardOutput = true,
RedirectStandardInput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
};
var process = new Process()
{
StartInfo = startInfo,
EnableRaisingEvents = true // Enable raising events to handle process exit
};
var tcs = new TaskCompletionSource<string>();
var fgThread = new Thread(() =>
{
var result = RunCommandXAsync(process, timeout, isInputEnable).ConfigureAwait(false).GetAwaiter().GetResult();
tcs.SetResult(result);
});
fgThread.IsBackground = false;
fgThread.SetApartmentState(ApartmentState.STA);
fgThread.Start();
fgThread.Join();
return tcs.Task.ConfigureAwait(false).GetAwaiter().GetResult();
}
catch (Exception ex)
{
Log.Exception(ex);
}
return String.Empty;
}
public async Task<String> RunCommandXAsync(Process process, TimeSpan timeout, bool isInputEnable = false)
{
try
{
var tcsExit = new TaskCompletionSource<int>();
var errorBuilder = new StringBuilder();
var outputBuilder = new StringBuilder();
// Event handler for error
process.ErrorDataReceived += (sender, e) =>
{
if (!String.IsNullOrEmpty(e.Data))
{
errorBuilder.AppendLine(e.Data);
}
};
// Event handler for output
process.OutputDataReceived += (sender, e) =>
{
if (!String.IsNullOrEmpty(e.Data))
{
outputBuilder.AppendLine(e.Data);
}
};
// Event handler for exit
process.Exited += (sender, e) =>
{
if (tcsExit.Task.IsCompleted)
{
tcsExit = new TaskCompletionSource<int>();
}
tcsExit.TrySetResult(process.ExitCode);
};
bool isProcessStarted = process.Start();
if (!isProcessStarted)
{
return String.Empty;
}
// Start the asynchronous read of the error stream.
process.BeginErrorReadLine();
// Start the asynchronous read of the output stream.
process.BeginOutputReadLine();
// Add process id devtunnel.exe
Thread.Sleep(TimeSpan.FromSeconds(2));
int processId = GetNgrokTunnelProcessId(Path.GetFileNameWithoutExtension(Common.NGROK_NAME));
if (processId != -1)
{
if (!_processIDs.Contains(processId))
{
_processIDs.Add(processId);
}
}
Task taskCompleted = await Task.WhenAny(tcsExit.Task, Task.Delay(timeout));
if (taskCompleted.IsCompletedSuccessfully)
{
var error = errorBuilder.ToString();
if (!String.IsNullOrEmpty(error))
{
// error
return error;
}
// output
var output = outputBuilder.ToString();
if (!String.IsNullOrEmpty(output))
{
// input
if (isInputEnable)
{
if (output.Contains("Are you sure? y/n"))
{
using (var writer = process.StandardInput)
{
if (writer is not null)
{
_ = writer.WriteLineAsync("Y");
writer.Close();
}
}
}
}
// output
return output;
}
}
}
catch (Exception ex)
{
Log.Exception(ex);
}
return String.Empty;
}
}
}
</code>
<code>using CardData.Ext.Logger; using CustomerCardServer.Ext.Mod.General; using CustomerCardServer.Properties; using System.Diagnostics; using System.Text; using System.Text.RegularExpressions; using File = System.IO.File; namespace CustomerCardServer.Ext.Mod.Tunnel { public class NgrokTunnelManager { private static NgrokTunnelManager? _instance; private static object _instanceLocker = new object(); private String? _appPath; private String? _ngrokTunnelFile; private String? _cmdFile; private List<int> _processIDs = new List<int>(); public List<int> ProcessIDs { get { return _processIDs; } private set { _processIDs = value; } } public String? SessionStatus { get; private set; } public String? PublicUrl { get; private set; } public String? PrivateUrl { get; private set; } private NgrokTunnelManager() { CopyCmdPromptFile(); CopyNgrokTunnelFile(); } public static NgrokTunnelManager GetInstance() { if (_instance is null) { lock (_instanceLocker) { _instance = new NgrokTunnelManager(); } } return _instance; } public bool CopyNgrokTunnelFile() { try { // App path _appPath = Application.StartupPath; if (String.IsNullOrEmpty(_appPath)) { MessageBox.Show("AppPath isn't valid!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } // ngrok file _ngrokTunnelFile = _appPath + Common.NGROK_NAME; if (String.IsNullOrEmpty(_ngrokTunnelFile)) { MessageBox.Show("Devtunnel.exe isn't found!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } // create ngrok file if (!File.Exists(_ngrokTunnelFile)) { File.WriteAllBytes(_ngrokTunnelFile, Resources.ngrok); } // save ngrok file path Settings.Default.NgrokFile = _ngrokTunnelFile; Settings.Default.Save(); return true; } catch (Exception ex) { Log.Exception(ex); } return false; } public bool CopyCmdPromptFile() { try { // App path _appPath = Application.StartupPath; if (String.IsNullOrEmpty(_appPath)) { MessageBox.Show("AppPath isn't valid!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } // cmd shortcut file _cmdFile = _appPath + Common.CMD_NAME; if (String.IsNullOrEmpty(_cmdFile)) { MessageBox.Show("Cmd.exe isn't found!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } // create cmd shortcut file if (!File.Exists(_cmdFile)) { File.WriteAllBytes(_cmdFile, Resources.cmd); } // save cmd shortcut file path Settings.Default.CmdFile = _cmdFile; Settings.Default.Save(); return true; } catch (Exception ex) { Log.Exception(ex); } return false; } public async Task<bool> AuthAlsoStartAsync(String authToken, String ipAddress, int port) { try { var argument1 = $"ngrok config add-authtoken {authToken}"; var argument2 = $"ngrok http https://{ipAddress}:{port}"; var authResult = await RunCommandAsync(argument1, TimeSpan.FromSeconds(5)); if (!authResult.Contains("Authtoken saved to configuration file")) { return false; } // BUG IN HERE, startResult IS ALWAYS EMPTY? var startResult = await RunCommandAsync(argument2, TimeSpan.FromSeconds(5)); if (String.IsNullOrEmpty(startResult)) { return false; } // Regex to extract Session Status String? sessionStatus = Regex.Match(startResult, @"Session Statuss+(w+)").Groups[1].Value; // Regex to extract Forwarding URLs Match? forwardingMatch = Regex.Match(startResult, @"Forwardings+(https?://[^s]+)s+->s+(https?://[^s]+)"); String? publicUrl = forwardingMatch.Groups[1].Value; String? privateUrl = forwardingMatch.Groups[2].Value; if (!String.IsNullOrEmpty(sessionStatus) && !String.IsNullOrEmpty(publicUrl) && !String.IsNullOrEmpty(privateUrl)) { SessionStatus = sessionStatus; PublicUrl = publicUrl; PrivateUrl = privateUrl; return true; } } catch (Exception ex) { Log.Exception(ex); } return false; } private int GetNgrokTunnelProcessId(string processName) { try { // Get the list of processes with the specified name Process[] processes = Process.GetProcessesByName(processName); if (processes.Length > 0) { // Return the PID of the first process found return processes[0].Id; } return -1; // Process not found } catch (Exception ex) { Log.Exception(ex); } return -1; } private void StopProcess() { try { // kill current devtunnel Process[] processes = Process.GetProcessesByName(Path.GetFileNameWithoutExtension(Common.NGROK_NAME)); foreach (Process process in processes) { if (process is null) { continue; } if (_processIDs is null) { break; } if (_processIDs.Count == 0) { break; } if (_processIDs.Contains(process.Id)) { process.Kill(); } } } catch (Exception ex) { Log.Exception(ex); } } public async Task<String> RunCommandAsync(String argument, TimeSpan timeout, bool isInputEnable = false) { try { StopProcess(); var startInfo = new ProcessStartInfo { FileName = "cmd.exe", Arguments = $"/c {argument}", RedirectStandardOutput = true, RedirectStandardInput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true, }; var process = new Process() { StartInfo = startInfo, EnableRaisingEvents = true // Enable raising events to handle process exit }; var tcs = new TaskCompletionSource<string>(); var fgThread = new Thread(() => { var result = RunCommandXAsync(process, timeout, isInputEnable).ConfigureAwait(false).GetAwaiter().GetResult(); tcs.SetResult(result); }); fgThread.IsBackground = false; fgThread.SetApartmentState(ApartmentState.STA); fgThread.Start(); fgThread.Join(); return tcs.Task.ConfigureAwait(false).GetAwaiter().GetResult(); } catch (Exception ex) { Log.Exception(ex); } return String.Empty; } public async Task<String> RunCommandXAsync(Process process, TimeSpan timeout, bool isInputEnable = false) { try { var tcsExit = new TaskCompletionSource<int>(); var errorBuilder = new StringBuilder(); var outputBuilder = new StringBuilder(); // Event handler for error process.ErrorDataReceived += (sender, e) => { if (!String.IsNullOrEmpty(e.Data)) { errorBuilder.AppendLine(e.Data); } }; // Event handler for output process.OutputDataReceived += (sender, e) => { if (!String.IsNullOrEmpty(e.Data)) { outputBuilder.AppendLine(e.Data); } }; // Event handler for exit process.Exited += (sender, e) => { if (tcsExit.Task.IsCompleted) { tcsExit = new TaskCompletionSource<int>(); } tcsExit.TrySetResult(process.ExitCode); }; bool isProcessStarted = process.Start(); if (!isProcessStarted) { return String.Empty; } // Start the asynchronous read of the error stream. process.BeginErrorReadLine(); // Start the asynchronous read of the output stream. process.BeginOutputReadLine(); // Add process id devtunnel.exe Thread.Sleep(TimeSpan.FromSeconds(2)); int processId = GetNgrokTunnelProcessId(Path.GetFileNameWithoutExtension(Common.NGROK_NAME)); if (processId != -1) { if (!_processIDs.Contains(processId)) { _processIDs.Add(processId); } } Task taskCompleted = await Task.WhenAny(tcsExit.Task, Task.Delay(timeout)); if (taskCompleted.IsCompletedSuccessfully) { var error = errorBuilder.ToString(); if (!String.IsNullOrEmpty(error)) { // error return error; } // output var output = outputBuilder.ToString(); if (!String.IsNullOrEmpty(output)) { // input if (isInputEnable) { if (output.Contains("Are you sure? y/n")) { using (var writer = process.StandardInput) { if (writer is not null) { _ = writer.WriteLineAsync("Y"); writer.Close(); } } } } // output return output; } } } catch (Exception ex) { Log.Exception(ex); } return String.Empty; } } } </code>
using CardData.Ext.Logger;
using CustomerCardServer.Ext.Mod.General;
using CustomerCardServer.Properties;
using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;
using File = System.IO.File;

namespace CustomerCardServer.Ext.Mod.Tunnel
{

    public class NgrokTunnelManager
    {
        private static NgrokTunnelManager? _instance;
        private static object _instanceLocker = new object();

        private String? _appPath;
        private String? _ngrokTunnelFile;
        private String? _cmdFile;

        private List<int> _processIDs = new List<int>();
        public List<int> ProcessIDs
        {
            get { return _processIDs; }
            private set { _processIDs = value; }
        }

        public String? SessionStatus { get; private set; }
        public String? PublicUrl { get; private set; }
        public String? PrivateUrl { get; private set; }

        private NgrokTunnelManager()
        {
            CopyCmdPromptFile();
            CopyNgrokTunnelFile();
        }

        public static NgrokTunnelManager GetInstance()
        {
            if (_instance is null)
            {
                lock (_instanceLocker)
                {
                    _instance = new NgrokTunnelManager();
                }
            }
            return _instance;
        }

        public bool CopyNgrokTunnelFile()
        {
            try
            {
                // App path
                _appPath = Application.StartupPath;
                if (String.IsNullOrEmpty(_appPath))
                {
                    MessageBox.Show("AppPath isn't valid!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return false;
                }
                // ngrok file
                _ngrokTunnelFile = _appPath + Common.NGROK_NAME;
                if (String.IsNullOrEmpty(_ngrokTunnelFile))
                {
                    MessageBox.Show("Devtunnel.exe isn't found!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return false;
                }
                // create ngrok file
                if (!File.Exists(_ngrokTunnelFile))
                {
                    File.WriteAllBytes(_ngrokTunnelFile, Resources.ngrok);
                }
                // save ngrok file path
                Settings.Default.NgrokFile = _ngrokTunnelFile;
                Settings.Default.Save();
                return true;
            }
            catch (Exception ex)
            {
                Log.Exception(ex);
            }
            return false;
        }

        public bool CopyCmdPromptFile()
        {
            try
            {
                // App path
                _appPath = Application.StartupPath;
                if (String.IsNullOrEmpty(_appPath))
                {
                    MessageBox.Show("AppPath isn't valid!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return false;
                }
                // cmd shortcut file
                _cmdFile = _appPath + Common.CMD_NAME;
                if (String.IsNullOrEmpty(_cmdFile))
                {
                    MessageBox.Show("Cmd.exe isn't found!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return false;
                }
                // create cmd shortcut file
                if (!File.Exists(_cmdFile))
                {
                    File.WriteAllBytes(_cmdFile, Resources.cmd);
                }
                // save cmd shortcut file path
                Settings.Default.CmdFile = _cmdFile;
                Settings.Default.Save();
                return true;
            }
            catch (Exception ex)
            {
                Log.Exception(ex);
            }
            return false;
        }

        public async Task<bool> AuthAlsoStartAsync(String authToken, String ipAddress, int port)
        {
            try
            {
                var argument1 = $"ngrok config add-authtoken {authToken}";
                var argument2 = $"ngrok http https://{ipAddress}:{port}";

                var authResult = await RunCommandAsync(argument1, TimeSpan.FromSeconds(5));
                if (!authResult.Contains("Authtoken saved to configuration file"))
                {
                    return false;
                }

                // BUG IN HERE, startResult IS ALWAYS EMPTY?
                var startResult = await RunCommandAsync(argument2, TimeSpan.FromSeconds(5));
                if (String.IsNullOrEmpty(startResult))
                {
                    return false;
                }

                // Regex to extract Session Status
                String? sessionStatus = Regex.Match(startResult, @"Session Statuss+(w+)").Groups[1].Value;

                // Regex to extract Forwarding URLs
                Match? forwardingMatch = Regex.Match(startResult, @"Forwardings+(https?://[^s]+)s+->s+(https?://[^s]+)");
                String? publicUrl = forwardingMatch.Groups[1].Value;
                String? privateUrl = forwardingMatch.Groups[2].Value;

                if (!String.IsNullOrEmpty(sessionStatus) && !String.IsNullOrEmpty(publicUrl) && !String.IsNullOrEmpty(privateUrl))
                {
                    SessionStatus = sessionStatus;
                    PublicUrl = publicUrl;
                    PrivateUrl = privateUrl;
                    return true;
                }
            }
            catch (Exception ex)
            {
                Log.Exception(ex);
            }
            return false;
        }


        private int GetNgrokTunnelProcessId(string processName)
        {
            try
            {
                // Get the list of processes with the specified name
                Process[] processes = Process.GetProcessesByName(processName);
                if (processes.Length > 0)
                {
                    // Return the PID of the first process found
                    return processes[0].Id;
                }
                return -1; // Process not found
            }
            catch (Exception ex)
            {
                Log.Exception(ex);
            }
            return -1;
        }

        private void StopProcess()
        {
            try
            {
                // kill current devtunnel
                Process[] processes = Process.GetProcessesByName(Path.GetFileNameWithoutExtension(Common.NGROK_NAME));
                foreach (Process process in processes)
                {
                    if (process is null)
                    {
                        continue;
                    }
                    if (_processIDs is null)
                    {
                        break;
                    }
                    if (_processIDs.Count == 0)
                    {
                        break;
                    }
                    if (_processIDs.Contains(process.Id))
                    {
                        process.Kill();
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Exception(ex);
            }
        }

        public async Task<String> RunCommandAsync(String argument, TimeSpan timeout, bool isInputEnable = false)
        {
            try
            {
                StopProcess();

                var startInfo = new ProcessStartInfo
                {
                    FileName = "cmd.exe",
                    Arguments = $"/c {argument}",
                    RedirectStandardOutput = true,
                    RedirectStandardInput = true,
                    RedirectStandardError = true,
                    UseShellExecute = false,
                    CreateNoWindow = true,
                };

                var process = new Process()
                {
                    StartInfo = startInfo,
                    EnableRaisingEvents = true  // Enable raising events to handle process exit
                };

                var tcs = new TaskCompletionSource<string>();
                var fgThread = new Thread(() =>
                {
                    var result = RunCommandXAsync(process, timeout, isInputEnable).ConfigureAwait(false).GetAwaiter().GetResult();
                    tcs.SetResult(result);
                });
                fgThread.IsBackground = false;
                fgThread.SetApartmentState(ApartmentState.STA);
                fgThread.Start();
                fgThread.Join();

                return tcs.Task.ConfigureAwait(false).GetAwaiter().GetResult();
            }
            catch (Exception ex)
            {
                Log.Exception(ex);
            }
            return String.Empty;
        }

        public async Task<String> RunCommandXAsync(Process process, TimeSpan timeout, bool isInputEnable = false)
        {
            try
            {
                var tcsExit = new TaskCompletionSource<int>();

                var errorBuilder = new StringBuilder();
                var outputBuilder = new StringBuilder();

                // Event handler for error
                process.ErrorDataReceived += (sender, e) =>
                {
                    if (!String.IsNullOrEmpty(e.Data))
                    {
                        errorBuilder.AppendLine(e.Data);
                    }
                };

                // Event handler for output
                process.OutputDataReceived += (sender, e) =>
                {
                    if (!String.IsNullOrEmpty(e.Data))
                    {
                        outputBuilder.AppendLine(e.Data);
                    }
                };

                // Event handler for exit
                process.Exited += (sender, e) =>
                {
                    if (tcsExit.Task.IsCompleted)
                    {
                        tcsExit = new TaskCompletionSource<int>();
                    }
                    tcsExit.TrySetResult(process.ExitCode);
                };

                bool isProcessStarted = process.Start();
                if (!isProcessStarted)
                {
                    return String.Empty;
                }

                // Start the asynchronous read of the error stream.
                process.BeginErrorReadLine();

                // Start the asynchronous read of the output stream.
                process.BeginOutputReadLine();

                // Add process id devtunnel.exe
                Thread.Sleep(TimeSpan.FromSeconds(2));
                int processId = GetNgrokTunnelProcessId(Path.GetFileNameWithoutExtension(Common.NGROK_NAME));
                if (processId != -1)
                {
                    if (!_processIDs.Contains(processId))
                    {
                        _processIDs.Add(processId);
                    }
                }

                Task taskCompleted = await Task.WhenAny(tcsExit.Task, Task.Delay(timeout));
                if (taskCompleted.IsCompletedSuccessfully)
                {
                    var error = errorBuilder.ToString();
                    if (!String.IsNullOrEmpty(error))
                    {
                        // error
                        return error;
                    }

                    // output
                    var output = outputBuilder.ToString();
                    if (!String.IsNullOrEmpty(output))
                    {
                        //  input
                        if (isInputEnable)
                        {
                            if (output.Contains("Are you sure? y/n"))
                            {
                                using (var writer = process.StandardInput)
                                {
                                    if (writer is not null)
                                    {
                                        _ = writer.WriteLineAsync("Y");
                                        writer.Close();
                                    }
                                }
                            }
                        }

                        // output
                        return output;
                    }
                }

            }
            catch (Exception ex)
            {
                Log.Exception(ex);
            }
            return String.Empty;
        }


    }
}

(Step 1) I use cmd prompt to call ngrok.exe to perform authentication with the command below:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>ngrok config add-authtoken 2mW3vWujIv8d1nNJkHyhEPvFcFF_84yExzagwLyL3EuHH4xDR
</code>
<code>ngrok config add-authtoken 2mW3vWujIv8d1nNJkHyhEPvFcFF_84yExzagwLyL3EuHH4xDR </code>
ngrok config add-authtoken 2mW3vWujIv8d1nNJkHyhEPvFcFF_84yExzagwLyL3EuHH4xDR

Result expected:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>Authtoken saved to configuration file:
C:UsersxxxxAppDataLocal/ngrok/ngrok.yml
</code>
<code>Authtoken saved to configuration file: C:UsersxxxxAppDataLocal/ngrok/ngrok.yml </code>
Authtoken saved to configuration file: 
C:UsersxxxxAppDataLocal/ngrok/ngrok.yml

(Step 2) I use cmd prompt to call ngrok.exe to deploy app online with the command below:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>ngrok http https://localhost:8080
</code>
<code>ngrok http https://localhost:8080 </code>
ngrok http https://localhost:8080

Result expected:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>ngrok (Ctrl+C to quit) Found a bug? Let us know: https://github.com/ngrok/ngrok Session Status online
Account [email protected] (Plan: Free)
Version 3.16.0
Region Asia (ap)
Latency 42ms
Web Interface http://127.0.0.1:4040
Forwarding https://fb82-202-154-12-130.ngrok-free.app -> https://127.0.0.1:8080 Connections ttl opn rt1 rt5 p50 p90
0 0 0.00 0.00 0.00 0.00
</code>
<code>ngrok (Ctrl+C to quit) Found a bug? Let us know: https://github.com/ngrok/ngrok Session Status online Account [email protected] (Plan: Free) Version 3.16.0 Region Asia (ap) Latency 42ms Web Interface http://127.0.0.1:4040 Forwarding https://fb82-202-154-12-130.ngrok-free.app -> https://127.0.0.1:8080 Connections ttl opn rt1 rt5 p50 p90 0 0 0.00 0.00 0.00 0.00 </code>
ngrok                                                                                                                     (Ctrl+C to quit)                                                                                                                                                                          Found a bug? Let us know: https://github.com/ngrok/ngrok                                                                                                                                                                                                                                                                                            Session Status            online                                                                                                                                      
Account                   [email protected] (Plan: Free)                                                                                                           
Version                   3.16.0
Region                    Asia (ap)                                                                                                                           
Latency                   42ms                                                                                                                                        
Web Interface             http://127.0.0.1:4040                                                                                                                       
Forwarding                https://fb82-202-154-12-130.ngrok-free.app -> https://127.0.0.1:8080                                                                                                                                                                                                                                                  Connections               ttl     opn     rt1     rt5     p50     p90                                                                                                                               
                          0       0       0.00    0.00    0.00    0.00  

2

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật