Skip to content

Commit

Permalink
Improve CLI ui
Browse files Browse the repository at this point in the history
  • Loading branch information
cristipufu committed Aug 28, 2024
1 parent 73f5ae7 commit 835c942
Show file tree
Hide file tree
Showing 10 changed files with 409 additions and 125 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ tunnelite http://localhost:3000

This command returns a public URL with an auto-generated subdomain, such as `https://abc123.tunnelite.com`.

![alt text](https://github.com/cristipufu/tunnelite/blob/master/docs/tunnelite-cli.gif?raw=true)

## How It Works

Tunnelite works by establishing a websocket connection to the public server and streaming all incoming data to your local application, effectively forwarding requests from the public URL to your local server.
Expand Down
Binary file added docs/tunnelite-cli.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
54 changes: 29 additions & 25 deletions src/Tunnelite.Client/HttpTunnel/HttpTunnelClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,18 @@

namespace Tunnelite.Client.HttpTunnel;

public class HttpTunnelClient
public class HttpTunnelClient : ITunnelClient
{
public event Func<Task>? Connected;
public HubConnection Connection { get; }
public string? TunnelUrl
{
get
{
return _currentTunnel?.TunnelUrl;
}
}

private static readonly HttpClientHandler LocalHttpClientHandler = new()
{
ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) => true,
Expand All @@ -19,10 +29,9 @@ public class HttpTunnelClient
private static readonly HttpClient LocalHttpClient = new(LocalHttpClientHandler);

private HttpTunnelResponse? _currentTunnel = null;
private readonly HubConnection Connection;
private readonly HttpTunnelRequest Tunnel;

public HttpTunnelClient(HttpTunnelRequest tunnel, LogLevel logLevel)
public HttpTunnelClient(HttpTunnelRequest tunnel, LogLevel? logLevel)
{
Tunnel = tunnel;

Expand All @@ -31,15 +40,18 @@ public HttpTunnelClient(HttpTunnelRequest tunnel, LogLevel logLevel)
.AddMessagePackProtocol()
.ConfigureLogging(logging =>
{
logging.SetMinimumLevel(logLevel);
logging.AddConsole();
if (logLevel.HasValue)
{
logging.SetMinimumLevel(logLevel.Value);
logging.AddConsole();
}
})
.WithAutomaticReconnect()
.Build();

Connection.On<HttpConnection>("NewHttpConnection", (httpConnection) =>
{
Console.WriteLine($"Received http tunneling request: [{httpConnection.Method}]{httpConnection.Path}");
Program.LogRequest(httpConnection.Method, httpConnection.Path);

_ = TunnelHttpConnectionAsync(httpConnection);

Expand All @@ -48,7 +60,7 @@ public HttpTunnelClient(HttpTunnelRequest tunnel, LogLevel logLevel)

Connection.On<WsConnection>("NewWsConnection", (wsConnection) =>
{
Console.WriteLine($"Received ws tunneling request: {wsConnection.Path}");
Program.LogRequest("WS", wsConnection.Path);

_ = TunnelWsConnectionAsync(wsConnection);

Expand All @@ -57,15 +69,11 @@ public HttpTunnelClient(HttpTunnelRequest tunnel, LogLevel logLevel)

Connection.Reconnected += async connectionId =>
{
Console.WriteLine($"Reconnected. New ConnectionId {connectionId}");

_currentTunnel = await RegisterTunnelAsync(tunnel);
};

Connection.Closed += async (error) =>
{
Console.WriteLine("Connection closed... reconnecting");

await Task.Delay(new Random().Next(0, 5) * 1000);

if (await ConnectWithRetryAsync(Connection, CancellationToken.None))
Expand Down Expand Up @@ -149,7 +157,7 @@ private async Task TunnelHttpConnectionAsync(HttpConnection httpConnection)
}
catch (Exception ex)
{
Console.WriteLine($"Unexpected error tunneling request: {ex.Message}");
Program.LogError($"[HTTP] Unexpected error tunneling request: {ex.Message}.");
using var errorRequest = new HttpRequestMessage(HttpMethod.Delete, requestUrl);
using var response = await ServerHttpClient.SendAsync(errorRequest);
}
Expand All @@ -172,13 +180,13 @@ private async Task TunnelWsConnectionAsync(WsConnection wsConnection)
}
catch (Exception ex)
{
Console.WriteLine($"Failed to connect to WebSocket for connection {wsConnection.RequestId}: {ex.Message}");
Program.LogError($"[WS] Failed to connect for connection {wsConnection.RequestId}: {ex.Message}.");
}
finally
{
cts.Cancel();

Console.WriteLine($"WS Connection {wsConnection.RequestId} done.");
Program.Log($"[WS] Connection {wsConnection.RequestId} closed.");
}
}

Expand Down Expand Up @@ -207,7 +215,7 @@ private async Task StreamIncomingWsAsync(WebSocket webSocket, WsConnection wsCon
}
finally
{
Console.WriteLine($"Writing data to WebSocket connection {wsConnection.RequestId} finished.");
Program.Log($"[WS] Writing data to connection {wsConnection.RequestId} finished.");
}
}

Expand Down Expand Up @@ -238,7 +246,7 @@ private async Task StreamOutgoingWsAsync(WebSocket localWebSocket, WsConnection
}
finally
{
Console.WriteLine($"Reading data from WebSocket connection {wsConnection.RequestId} finished.");
Program.Log($"[WS] Reading data from connection {wsConnection.RequestId} finished.");

ArrayPool<byte>.Shared.Return(buffer);
}
Expand All @@ -258,18 +266,14 @@ private async Task StreamOutgoingWsAsync(WebSocket localWebSocket, WsConnection

tunnelResponse = await response.Content.ReadFromJsonAsync<HttpTunnelResponse?>();

if (response.IsSuccessStatusCode)
{
Console.WriteLine($"Tunnel created successfully: {tunnelResponse!.TunnelUrl}");
}
else
if (!response.IsSuccessStatusCode)
{
Console.WriteLine($"{tunnelResponse!.Message}:{tunnelResponse.Error}");
Program.LogError($"{tunnelResponse!.Message}:{tunnelResponse.Error}");
}
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred while registering the tunnel {ex.Message}");
Program.LogError($"[HTTP] An error occurred while registering the tunnel {ex.Message}.");

await Task.Delay(5000);
}
Expand All @@ -286,7 +290,7 @@ private async Task<bool> ConnectWithRetryAsync(HubConnection connection, Cancell
{
await connection.StartAsync(token);

Console.WriteLine($"Client connected to SignalR hub. ConnectionId: {connection.ConnectionId}");
Connected?.Invoke();

return true;
}
Expand All @@ -296,7 +300,7 @@ private async Task<bool> ConnectWithRetryAsync(HubConnection connection, Cancell
}
catch
{
Console.WriteLine($"Cannot connect to WebSocket server on {Tunnel.PublicUrl}");
Program.LogError($"[HTTP] Cannot connect to the public server on {Tunnel.PublicUrl}.");

await Task.Delay(5000, token);
}
Expand Down
14 changes: 14 additions & 0 deletions src/Tunnelite.Client/ITunnelClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Microsoft.AspNetCore.SignalR.Client;

namespace Tunnelite.Client;

public interface ITunnelClient
{
event Func<Task>? Connected;

HubConnection Connection { get; }

string? TunnelUrl { get; }

Task ConnectAsync();
}
Loading

0 comments on commit 835c942

Please sign in to comment.